Components Menus
Menus are collections of links or buttons.

Vertical menu

The vertical menu supports nested menu items, grouped menu items and live patching. You can use this in your main layout sidebar or within a page.

Router
          <div class="w-96">
  <.vertical_menu
    title="Main menu"
    current_page={:home}
    menu_items={[
      %{
        name: :home,
        label: "Home",
        path: ~p"/",
        icon: :home
      },
      %{
        name: :data,
        label: "Data",
        path: ~p"/",
        icon: :circle_stack,
        menu_items: [
          %{
            name: :data,
            label: "Archived",
            path: ~p"/",
            icon: :archive_box
          },
          %{
            name: :data,
            label: "Downloads",
            path: ~p"/",
            icon: :arrow_down_on_square_stack
          },
        ]
      },
      %{
        name: :tools,
        label: "Tools",
        path: ~p"/",
        icon: :wrench
      },
    ]}
  />
</div>

        

Menu items

Every menu item is represented by a map in the list you pass to the menu_items attr. This map is passed to a <.vertical_menu_item /> component, which has the following attrs you can pass in:

attr :path, :string, default: nil
attr :icon, :any, default: nil, doc: "an atom representing a Heroicon OR a function pointing to a function component that renders an icon OR a HTML string representing an icon"
attr :label, :string
attr :name, :atom, default: nil, doc: "this is what is matched to `current_page` on the <.vertical_menu />"
attr :menu_items, :list, default: nil, doc: "a list of submenu items accessible via dropdown. Uses Alpine JS"
attr :patch_group, :atom, default: nil, doc: "if several menu items point to the same live_view, you can match them up for live patching by giving them the same atom here"

Live patching

By default, links are “live_redirects” (which will work for dead views too). Sometimes you might want to use live_patching to speed things up.

Let’s say you have three menu items that point to the same live view. In this case we can utilize a live_patch link. To do this, you add the patch_group key to the menu item.

[
  %{name: :one, label: "One", path: "/one", icon: :key, patch_group: :my_unique_group},
  %{name: :two, label: "Two", path: "/two", icon: :key, patch_group: :my_unique_group},
  %{name: :three, label: "Three", path: "/three", icon: :key, patch_group: :my_unique_group},
  %{name: :another_link, label: "Other", path: "/other", icon: :key},
]

Now, if you’re on page :one, and click a link in the menu to either :two, or :three, the live view will be patched because they are in the same patch_group. If you click :another_link, the live view will be redirected.

Icons

The icon should match to a Heroicon (Petal Components must be installed). If you have your own icon, you can pass a function to the icon attribute instead of an atom:

[
  %{
    name: :sign_in,
    label: "Sign in",
    path: "/sign-in",
    icon: &my_cool_icon/1,
  }
]

Or just pass a string of HTML:

[
  %{
    name: :sign_in,
    label: "Sign in",
    path: "/sign-in,
    icon: "<svg>...</svg>",
  }
]

Nested menu items

You can have nested menu items that will be displayed in a dropdown menu. To do this, you add a menu_items key to the menu item. eg:

[
  %{
    name: :auth,
    label: "Auth",
    icon: :key,
    menu_items: [
      %{
        name: :sign_in,
        label: "Sign in",
        path: "/sign-in",
        icon: :key,
      },
      %{
        name: :sign_up,
        label: "Sign up",
        path: "/sign-up",
        icon: :key,
      },
    ]
  }
]

Menu groups

Vertical menu supports multi menu groups. eg:

User

  • Profile
  • Settings

Company

  • Dashboard
  • Company Settings

To enable this, change the structure of main_menu_items to this:

main_menu_items = [
  %{
    title: "Menu group 1",
    menu_items: [ ... menu items ... ]
  },
  %{
    title: "Menu group 2",
    menu_items: [ ... menu items ... ]
  },
]

Example:

          <div class="w-96">
  <.vertical_menu
    title="Main menu"
    menu_items={[
      %{
        title: "Main",
        menu_items: [
      %{
        name: :home,
        label: "Home",
        path: ~p"/",
        icon: :home
      },
      %{
        name: :data,
        label: "Data",
        path: ~p"/",
        icon: :circle_stack,
        menu_items: [
          %{
            name: :data,
            label: "Archived",
            path: ~p"/",
            icon: :archive_box
          },
          %{
            name: :data,
            label: "Downloads",
            path: ~p"/",
            icon: :arrow_down_on_square_stack
          },
        ]
      },
      %{
        name: :tools,
        label: "Tools",
        path: ~p"/",
        icon: :wrench
      },
    ]
      },
      %{
        title: "User",
        menu_items: [
          %{
            name: :profile,
            label: "Profile",
            path: ~p"/",
            icon: :cog_6_tooth
          },
        ]
      }
    ]}
    current_page={:home}
  />
</div>

        

Vertical menu properties

          # <.vertical_menu />
attr :menu_items, :list, required: true
attr :current_page, :atom, required: true
attr :title, :string, default: nil

attr(:js_lib, :string,
  default: "alpine_js",
  values: ["alpine_js", "live_view_js"],
  doc: "javascript library used for toggling"
)

# Individual menu items:
attr :path, :string, default: nil
attr :icon, :any, default: nil, doc: "an atom representing a Heroicon OR a function pointing to a function component that renders an icon OR a HTML string representing an icon"
attr :label, :string
attr :name, :atom, default: nil, doc: "this is what is matched to `current_page` on the <.vertical_menu />"
attr :menu_items, :list, default: nil, doc: "a list of submenu items accessible via dropdown. Uses Alpine JS"
attr :patch_group, :atom, default: nil, doc: "if several menu items point to the same live_view, you can match them up for live patching by giving them the same atom here"