# Tailwind Form Field components

**URL:** <https://forum.elixirforum.com/t/tailwind-form-field-components/48308>\
**Category:** Questions\
**Tags:** forms, phoenix, liveview, tailwindcss\
**Created:** [June 10, 2022, 7:29am UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308 "2022-06-10T07:29:59Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![hvanderheide](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/h/59ef9b/32.png) [@hvanderheide](https://forum.elixirforum.com/u/hvanderheide)\
**Post date:** [June 10, 2022, 7:29am UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/1 "2022-06-10T07:29:59Z")

</div>

I’m trying to get some form styling going with tailwind in a (live) view. The approach below works, however the heex template seems to get really cluttered with repetitive code, especially as I introduce error stylings. As tailwind seems generally common within the phoenix world, I thought there might be a tailwind form field renderer already, but I was unable to find one.

```elixir
 <div class="col-span-6">
  <%= label f, :email, class: ["block text-sm font-medium text-gray-700"] %>
  <div class="mt-1 sm:mt-0 sm:col-span-2 relative rounded-md shadow-sm">
    <%= email_input f, :email, required: true, autocomplete: false, value: @card.email, class: ["mt-1 block w-full shadow-sm sm:text-sm rounded-md"] ++ [(if f.errors[:email], do: "pr-10 border-red-300 text-red-900 placeholder-red-300 focus:outline-none focus:ring-red-500 focus:border-red-500", else: "focus:ring-indigo-500 focus:border-indigo-500 shadow-sm border-gray-300")] %>
    <%= if f.errors[:email] do %>
      <div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
        <svg class="h-5 w-5 text-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
          <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
        </svg>
      </div>
    <% end %>
  </div>
  <%= if message = f.errors[:email] do %>
    <p class="mt-2 text-sm text-red-600" id="email-error"><%= translate_error(message) %></p>
  <% end %>
</div>

```

So I was thinking, coming from python webframeworks, I should probably be able to create a FormFieldComponent and hand it the form, field(name, type) and instance to render properly. Without taking fieldsets into account that would probably lead to a template with a bunch of live\_component tags.

Is this a good approach, or is there a more common way to do this?

Edit: It seems petal.build kind of solved this problem, although I’m not sure they fully adopted tailwind’s styling (e.g. svg elements seem missing from formfield errors)

---

<div class="post-metadata">

**Author:** ![MariaKir](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mariakir/32/26783_2.png) [@MariaKir](https://forum.elixirforum.com/u/MariaKir)\
**Post date:** [June 10, 2022, 3:08pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/2 "2022-06-10T15:08:23Z")

</div>

I would choose another approach and simply collect all these in Tailwind components. In your assets/css there should be an app.css file with components. You can add new ones to it such as:

```elixir
.form-label {
    @apply
        block
        text-sm
        font-medium
        text-gray-700
}

```

As for your svg and error objects, it’s possible to utilise what Phoenix already provides. I’ve made an svg helper:

```elixir
defmodule ProjectName.IconHelper do
  @moduledoc """
  Give some icons to be used on templates.
  """

  use Phoenix.HTML

  alias ProjectName.Router.Helpers, as: Routes

  def icon_tag(name, opts \\ []) do
    id = Keyword.get(opts, :id, "")
    classes = Keyword.get(opts, :class, "")

    content_tag(:svg, id: id, class: classes) do
      tag(:use, href: Routes.static_path(ProjectName.Endpoint, "/svg/#{name}.svg"))
    end
  end
end

```

As well as an error helper based on the one Phoenix provides:

```elixir
  def error_label_style(form, field) do
    form.errors
    |> Keyword.get_values(field)
    |> Enum.map(fn _error ->
      "invalid-feedback-label"
    end)
  end

```

with the component

```elixir
.invalid-feedback-label {
    @apply
        text-red-500
}

```

I hope this was at all helpful and that I didn’t misunderstand you too bad. 😅  
My team uses Tailwind and really enjoy it. We use a mix of components and just writing Tailwind directly into the HTML depending on whether we’re going to use this styling a lot (then it goes into a component) or if it’s single use (then it goes into the html).

---

<div class="post-metadata">

**Author:** ![chrismccord](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/chrismccord/32/24233_2.png) [@chrismccord](https://forum.elixirforum.com/u/chrismccord)\
**Post date:** [June 10, 2022, 3:18pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/3 "2022-06-10T15:18:59Z")

</div>

You want to compartmentalize your forms and styling into their own function components. For example, you templates should look something like this:

```heex
    <.form let={f} for={@changeset}>
      <.input f={f} field={:email} type="email" value={@card.email} />
      <.input f={f} field={:title} type="text />
    </.form>

```

and you can define input functions which wraps all your styling and error tags:

```elixir
  def input(%{type: "email"} = assigns) do
    ~H"""
    <%= label @f, @field, class: ["block text-sm font-medium text-gray-700"] %>
    <div class="mt-1 sm:mt-0 sm:col-span-2 relative rounded-md shadow-sm">
      <%= email_input @f, @field, required: true, autocomplete: false, value: @value, class: ["mt-1 block w-full shadow-sm sm:text-sm rounded-md"] ++ [(if f.errors[:email], do: "pr-10 border-red-300 text-red-900 placeholder-red-300 focus:outline-none focus:ring-red-500 focus:border-red-500", else: "focus:ring-indigo-500 focus:border-indigo-500 shadow-sm border-gray-300")] %>
      <%= if @f.errors[@field] do %>
        <div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
          <svg class="h-5 w-5 text-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
            <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
          </svg>
        </div>
      <% end %>
    </div>
    """
  end

  def input(%{type: "text"} = assigns) do
    ...
  end

```

---

<div class="post-metadata">

**Author:** ![cvkmohan](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/cvkmohan/32/39944_2.png) [@cvkmohan](https://forum.elixirforum.com/u/cvkmohan)\
**Post date:** [June 10, 2022, 3:23pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/4 "2022-06-10T15:23:52Z")

</div>

I think, in addition to this, better to define some `@apply` directives for `invalid-feedback`, `form-control` etc. Otherwise, repeating those long classes for `email_input`, `text_input` etc. might be a lot of duplicate code - and - changing a border color or something might be difficult.  
Ofcourse, that part goes into the `tailwind` part of it - and - `phoenix` form markup has nothing to do with it.

---

<div class="post-metadata">

**Author:** ![chrismccord](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/chrismccord/32/24233_2.png) [@chrismccord](https://forum.elixirforum.com/u/chrismccord)\
**Post date:** [June 10, 2022, 3:36pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/5 "2022-06-10T15:36:18Z")

</div>

To each their own. With tailwind I never touch css files 🙂  
No problem going that direction if you want to, but I found function components are enough to avoid duplication that I care about.

---

<div class="post-metadata">

**Author:** ![cvkmohan](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/cvkmohan/32/39944_2.png) [@cvkmohan](https://forum.elixirforum.com/u/cvkmohan)\
**Post date:** [June 10, 2022, 3:41pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/6 "2022-06-10T15:41:04Z")

</div>

Not even

```elixir
@label_class "block text-sm font-medium text-gray-700"

```

?  
I mean - for the label class, if I want to add `dark:text-gray-200`? This is tangential to the question and context. But, just want to pick your thought process.

---

<div class="post-metadata">

**Author:** ![chrismccord](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/chrismccord/32/24233_2.png) [@chrismccord](https://forum.elixirforum.com/u/chrismccord)\
**Post date:** [June 10, 2022, 3:47pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/7 "2022-06-10T15:47:58Z")

</div>

yeah to be clear, I’m saying I can de-dup within elixir rather than using css rules, for example using your label\_class example and the upcoming 0.18 declarative assigns:

```elixir
  attr :type, :string, required: true
  attr :label_class, :string, default: "block text-sm font-medium text-gray-700"

```

Or, you could support an optional `<:label>` slot which the caller can use to pass whatever they wanted, so you have caller customization options depending on what you want.

---

<div class="post-metadata">

**Author:** ![hvanderheide](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/h/59ef9b/32.png) [@hvanderheide](https://forum.elixirforum.com/u/hvanderheide)\
**Post date:** [June 10, 2022, 6:07pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/8 "2022-06-10T18:07:11Z")

</div>

Thanks for all the replies (everyone!) this is kind of what I ended up doing

---

<div class="post-metadata">

**Author:** ![cvkmohan](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/cvkmohan/32/39944_2.png) [@cvkmohan](https://forum.elixirforum.com/u/cvkmohan)\
**Post date:** [July 23, 2022, 11:50am UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/9 "2022-07-23T11:50:44Z")

</div>

@chrismccord Is this the still best way to write an input function component? Or in view of [Phoenix LiveView Tailwind Variants · Fly](https://fly.io/phoenix-files/phoenix-liveview-tailwind-variants/) - using plugins a more elegant solution can be written?

---

<div class="post-metadata">

**Author:** ![chrismccord](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/chrismccord/32/24233_2.png) [@chrismccord](https://forum.elixirforum.com/u/chrismccord)\
**Post date:** [July 23, 2022, 2:11pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/10 "2022-07-23T14:11:18Z")

</div>

Yes your function component would make use of this internally but the above comment still applies

---

<div class="post-metadata">

**Author:** ![cvkmohan](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/cvkmohan/32/39944_2.png) [@cvkmohan](https://forum.elixirforum.com/u/cvkmohan)\
**Post date:** [July 21, 2024, 12:37pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/11 "2024-07-21T12:37:54Z")

</div>

@chrismccord Now that Phoenix uses `used_field` and depracated `phx-feedback-for` - how do we use it to show a tick mark when the input is valid?  
In the above snippet that you have given, when there is an error it shows an error icon - inside the textbox. I want to show a tick icon when the field is valid - and - the field stays normal when it is untouched.

---

<div class="post-metadata">

**Author:** ![cvkmohan](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/cvkmohan/32/39944_2.png) [@cvkmohan](https://forum.elixirforum.com/u/cvkmohan)\
**Post date:** [July 23, 2024, 3:44pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/12 "2024-07-23T15:44:14Z")

</div>

From the Phoenix LiveView documentation,

```elixir
<input type="text" name={@form[:title].name} value={@form[:title].value} />

<div :if={used_input?(@form[:title])}>
  <p :for={error <- @form[:title].errors}><%= error %></p>
</div>

<input type="text" name={@form[:email].name} value={@form[:email].value} />

<div :if={used_input?(@form[:email])}>
  <p :for={error <- @form[:email].errors}><%= error %></p>
</div>

```

This can be used at the form level. However, if you want to handle at the input component level, how do we use `used_input/1`? In other languages we can give `used_input(self)` or something similar. How to do it here?

---

<div class="post-metadata">

**Author:** ![LostKobrakai](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/lostkobrakai/32/3072_2.png) [@LostKobrakai](https://forum.elixirforum.com/u/LostKobrakai)\
**Post date:** [July 23, 2024, 3:47pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/13 "2024-07-23T15:47:45Z")

</div>

Phoenix has been updated to generate code using `used_input?`:

> **[PhoenixDiff · 
      v1.7.12 to v1.7.13](https://www.phoenixdiff.org/compare/1.7.12...1.7.13)**
>
> See the changes needed to upgrade your Phoenix application

---

<div class="post-metadata">

**Author:** ![cvkmohan](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/cvkmohan/32/39944_2.png) [@cvkmohan](https://forum.elixirforum.com/u/cvkmohan)\
**Post date:** [July 23, 2024, 4:04pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/14 "2024-07-23T16:04:50Z")

</div>

I guess I did not place the question clearly enough.  
Presently, `core_components.ex` contains an implementation of `input`

```elixir
def input(assigns) do
    ~H"""
    <div>
      <.label for={@id}><%= @label %></.label>
      <input
        type={@type}
        name={@name}
        id={@id}
        value={Phoenix.HTML.Form.normalize_value(@type, @value)}
        class={[
          "block w-full rounded-lg px-4 py-3 text-sm disabled:pointer-events-none disabled:opacity-50",
          @errors == [] &&
            "border-gray-200 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-700 dark:bg-slate-900 dark:text-gray-400 dark:focus:ring-gray-600",
          @errors != [] &&
            "border-red-500 focus:border-red-500 focus:ring-red-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400"
        ]}
        {@rest}
      />
      <.error :for={msg <- @errors}><%= msg %></.error>
    </div>
    """
  end

```

Here we are only dealing with 2 conditions. `@errors == []` and `@errors != []` .  
What if I want to show a tick mark or different borders when the input is validated? How do I use `used_input/1` here in this function?

---

<div class="post-metadata">

**Author:** ![LostKobrakai](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/lostkobrakai/32/3072_2.png) [@LostKobrakai](https://forum.elixirforum.com/u/LostKobrakai)\
**Post date:** [July 23, 2024, 4:07pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/15 "2024-07-23T16:07:29Z")

</div>

You need to look at the first function head for `<.input>`. It’s currently only using `used_input?` to determine if `@errors` should be filled or empty. You can customize the code there to add another assign like `valid?`, which is only set if the input was used and has no errors.

---

<div class="post-metadata">

**Author:** ![cvkmohan](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/cvkmohan/32/39944_2.png) [@cvkmohan](https://forum.elixirforum.com/u/cvkmohan)\
**Post date:** [July 23, 2024, 4:11pm UTC](https://forum.elixirforum.com/t/tailwind-form-field-components/48308/16 "2024-07-23T16:11:25Z")

</div>

Got it. Thanks.
