Handling classes in Phoenix LiveView components

2 min read

With Phoenix LiveView you can build components that encapsulate styling. This is especially useful when using a class-based CSS framework like Tailwind CSS.

Often you will want to make your component configurable by allowing the users of the component to pass in additional classes. The CoreComponents module that is created with your first LiveView-generated files demonstrates one way to accomplish this with the use of lists. In the contrived example below a string class attribute can optionally be provided to the component:

attr :datetime, :string, required: true
attr :class, :string, default: nil

def time(assigns) do
    ~H"""
        <time datetime={@datetime} class={["font-medium text-sm", @class]} />
    """
end

# Call the component and make the text red
<.time datetime="2023-03-21" class="text-red-500" />

However, this doesn't handle more advanced uses of the component. If, for example, you wanted to apply conditional classes, you are not able to do so easily.

However, we can change the class attribute to a list, allowing us to pass a list of strings. This allows users of the component to more easily compose classes in their code.

attr :datetime, :string, required: true
attr :class, :any, default: []

def time(assigns) do
    ~H"""
    <time
        datetime={@datetime}
        class={[
            "font-medium text-sm" | List.wrap(@class)
        ]}
    />
    """
end

# You can still call the component with a string
<.time datetime="2023-03-21" class="text-red-500" />

# But now you can also provide a list allowing for more control
<.time datetime="2024-03-21" class={["text-red-500", @value && "bg-gray-500"]} />

This small change has improved the API for my components. I hope it benefits you as well.