Good way to render active nav bar item

I use a component to render the top navigation bar. That bar contains three menu items. I’d like to highlight the current item with is_active={true}.

My problem is that I have no idea how to call the component with the needed information having a clean and easy to understand code solution which doesn’t contain of a million if/else structures.

I would like to do something like <.top_navigation active_tab="home" /> or something similar. I can solve this but my solution is not elegant (a lot of if/else). What is an elegant solution for this problem?

  @doc """
  Top navigation bar.

  ## Examples

      <.top_navigation />
  """
  attr :active_nav_tab, :string, default: "", doc: "current navigation tab"
  attr :current_user, :map, default: %{}, doc: "current user"

  def top_navigation(assigns) do
    ~H"""
    <div class="border-y border-brand-silver-100">
      <nav class="grid grid-cols-3 gap-1">
        <.home_nav_item is_active={true} />
        <.chat_nav_item current_user ={@current_user} />
        <.user_profile_item current_user ={@current_user} />
      </nav>
    </div>
    """
  end

I’d summarise your issue as the following: You’re encoding information about individual nav items in their component name, which your code doesn’t have access to. You see them when reading the component, but that’s it. So you’re left with a manual if statement per nav item.

Instead of creating concrete nav item components you likely want a single abstract nav item component, which you then make concrete by additional data you provide.

E.g. I have this in a few poc’s I built:

def main_nav(assigns) do
    items = [
      %{label: "Add item", href: ~p"/items/new", active: false},
      %{label: "Add wardrobe", href: "/", active: false},
      %{label: "Add category", href: "/", active: false}
    ]

    assigns = assign(assigns, :items, items)

    ~H"""
    <.link
      :for={item <- @items}
      navigate={item.href}
      class={[
        "block px-4 py-2 text-sm text-gray-700",
        if(item.active, do: "bg-gray-100")
      ]}
    >
      <%= item.label %>
    </.link>
    """
  end
1 Like

To add to the comment from @LostKobrakai, if you need to determine the current active tab, you could use live view hooks - this article explains it: Active nav with LiveView · The Phoenix Files

2 Likes