NavBuddy

Hex version badgeHex docs badgeLicense badge

Advanced Configurable Navigation Component for Phoenix LiveView

NavBuddy provides comprehensive navigation components for Phoenix LiveView applications, featuring dropdowns, mega menus, mobile-responsive design, native DaisyUI integration, and full accessibility support.

Features

Quick Start

Add to your mix.exs:

def deps do
  [
    {:navbuddy, "~> 0.4.0"}
  ]
end

Installation & Setup

Choose your integration approach:

🚀 Native DaisyUI Integration (Recommended for Phoenix 1.8+)

For projects with Tailwind CSS + DaisyUI already set up:

# Add to mix.exs
{:navbuddy, "~> 0.4.0"}

Then use native components:

<NavBuddy.NativeComponent.native_nav
  items={@nav_items}
  brand={%{name: "MyApp", href: "/"}}
  theme="light"
/>

📖 Complete Native Setup Guide

With DaisyUI (Recommended)

If you're using DaisyUI in your Phoenix project, NavBuddy will automatically integrate with your existing themes and styling:

<!-- NavBuddy CSS will work seamlessly with your DaisyUI setup -->

<link rel="stylesheet" href="/assets/navbuddy.css" />
<script src="/assets/navbuddy.js"></script>

Standalone (Without DaisyUI)

Include the CSS and JavaScript in your layout:

<link rel="stylesheet" href="/assets/navbuddy.css" />
<script src="/assets/navbuddy.js"></script>

Use in your LiveView:

defmodule MyAppWeb.PageLive do
  use MyAppWeb, :live_view
  import NavBuddy.Component

  def mount(_params, _session, socket) do
    socket = assign(socket, :navigation_items, navigation_items())
    {:ok, socket}
  end

  def render(assigns) do
    ~H"""
    <.nav_menu items={@navigation_items} current_path={@current_path} />
    """
  end

  defp navigation_items do
    [
      %{id: "home", label: "Home", navigate: "/"},
      %{id: "about", label: "About", navigate: "/about"},
      %{
        id: "products",
        label: "Products",
        navigate: "/products",
        dropdown: [
          %{id: "web", label: "Web Apps", navigate: "/products/web"},
          %{id: "mobile", label: "Mobile Apps", navigate: "/products/mobile"}
        ]
      }
    ]
  end
end

Navigation Structure

NavBuddy supports various navigation structures with comprehensive validation and helpful error messages.

Required vs Optional Fields

Required fields:

Optional fields:

Convenience Functions

NavBuddy provides helper functions to create navigation items easily:

# Simple navigation item
NavBuddy.nav_item("home", "Home", "/")
# => %{id: "home", label: "Home", navigate: "/"}

# With additional options
NavBuddy.nav_item("admin", "Admin", "/admin", permissions: "admin", icon: "admin-icon")
# => %{id: "admin", label: "Admin", navigate: "/admin", permissions: "admin", icon: "admin-icon"}

# Dropdown item
dropdown_items = [
  NavBuddy.nav_item("web", "Web Apps", "/products/web"),
  NavBuddy.nav_item("mobile", "Mobile Apps", "/products/mobile")
]
NavBuddy.dropdown_item("products", "Products", dropdown_items)

# Mega menu item
categories = %{
  "Frontend" => [NavBuddy.nav_item("react", "React", "/tools/react")],
  "Backend" => [NavBuddy.nav_item("elixir", "Elixir", "/tools/elixir")]
}
NavBuddy.mega_menu_item("tools", "Tools", categories)

Validation and Error Handling

NavBuddy provides comprehensive validation with helpful error messages:

# Invalid item - missing required fields
NavBuddy.validate_nav_item(%{label: "Home"})
# => {:error, "Navigation item missing required field &#39;id&#39;. Required fields: id (string), label (string). Optional fields: navigate, icon, dropdown, mega_menu, active, disabled, permissions"}

# Invalid field type
NavBuddy.validate_nav_item(%{id: "home", label: "Home", active: "yes"})
# => {:error, "Field &#39;active&#39; must be a boolean, got: \"yes\""}

# Conflicting navigation types
NavBuddy.validate_nav_item(%{id: "item", label: "Item", dropdown: [], mega_menu: %{}})
# => {:error, "Navigation item cannot have both &#39;dropdown&#39; and &#39;mega_menu&#39;. Choose one or the other."}

Simple Links

%{id: "home", label: "Home", navigate: "/", icon: "home"}

Single-Layer Dropdowns

%{
  id: "products",
  label: "Products",
  navigate: "/products",
  icon: "package",
  dropdown: [
    %{id: "web", label: "Web Development", navigate: "/products/web"},
    %{id: "mobile", label: "Mobile Apps", navigate: "/products/mobile"}
  ]
}

Multi-Layer Dropdowns

%{
  id: "services",
  label: "Services",
  dropdown: [
    %{
      id: "development",
      label: "Development",
      dropdown: [
        %{id: "frontend", label: "Frontend", navigate: "/services/frontend"},
        %{id: "backend", label: "Backend", navigate: "/services/backend"}
      ]
    }
  ]
}

Mega Menus

%{
  id: "solutions",
  label: "Solutions",
  mega_menu: %{
    title: "Our Solutions",
    description: "Comprehensive business solutions",
    sections: [
      %{
        title: "For Small Business",
        description: "Perfect for startups",
        items: [
          %{id: "starter", label: "Starter Package", navigate: "/solutions/starter"},
          %{id: "growth", label: "Growth Package", navigate: "/solutions/growth"}
        ]
      },
      %{
        title: "For Enterprise",
        description: "Scalable solutions",
        items: [
          %{id: "enterprise", label: "Enterprise Suite", navigate: "/solutions/enterprise"},
          %{id: "custom", label: "Custom Solutions", navigate: "/solutions/custom"}
        ]
      }
    ]
  }
}

Permissions-Based Navigation

NavBuddy supports permissions-based filtering to control which navigation items are visible to users. Add a permissions key to any navigation item to require specific permissions.

Basic Permissions

navigation_items = [
  %{id: "home", label: "Home", navigate: "/"},
  %{id: "dashboard", label: "Dashboard", navigate: "/dashboard", permissions: "user"},
  %{id: "admin", label: "Admin Panel", navigate: "/admin", permissions: "admin"}
]

Multiple Required Permissions

Use a list to require multiple permissions (user must have ALL permissions):

navigation_items = [
  %{
    id: "sensitive",
    label: "Sensitive Data",
    navigate: "/sensitive",
    permissions: ["admin", "data_access"]  # Requires BOTH permissions
  }
]

Nested Structure Permissions

Permissions work with dropdowns and mega-menus:

navigation_items = [
  %{
    id: "reports",
    label: "Reports",
    dropdown: [
      %{id: "basic_reports", label: "Basic Reports", navigate: "/reports/basic"},
      %{id: "admin_reports", label: "Admin Reports", navigate: "/reports/admin", permissions: "admin"},
      %{id: "analytics", label: "Analytics", navigate: "/reports/analytics", permissions: ["analyst", "premium"]}
    ]
  }
]

Using in LiveView

Pass user permissions to the component:

# In your LiveView mount/3 or assign
def mount(_params, session, socket) do
  current_user = get_current_user(session)
  user_permissions = get_user_permissions(current_user)

  socket = assign(socket, user_permissions: user_permissions)
  {:ok, socket}
end

# In your template
<NavBuddy.Component.nav_menu
  items={@navigation_items}
  user_permissions={@user_permissions}
  config={@nav_config}
  current_path={@current_path} />

Permission Examples

# String permission - user needs "admin" permission
%{id: "admin", label: "Admin", navigate: "/admin", permissions: "admin"}

# List of permissions - user needs ALL listed permissions
%{id: "sensitive", label: "Sensitive", navigate: "/sensitive", permissions: ["admin", "security"]}

# No permissions - visible to everyone
%{id: "home", label: "Home", navigate: "/"}

# nil permissions - same as no permissions key
%{id: "public", label: "Public", navigate: "/public", permissions: nil}

The filtering automatically:

Configuration

Create a configuration struct:

config = %NavBuddy.Config{
  orientation: :horizontal,        # :horizontal | :vertical
  theme: :auto,                   # :light | :dark | :auto
  dropdown_trigger: :hover,       # :hover | :click
  mobile_behavior: :drawer,       # :drawer | :collapse | :overlay
  animations: true,               # boolean
  accessibility: true,            # boolean
  mobile_breakpoint: 768,         # pixels
  max_dropdown_depth: 5,          # levels
  auto_close_delay: 300,          # milliseconds
  touch_gestures: true,           # boolean
  analytics: false                # boolean
}

Use with your navigation:

<.nav_menu items={@navigation_items} config={config} current_path={@current_path} />

LiveView Integration

NavBuddy includes comprehensive LiveView helpers:

defmodule MyAppWeb.PageLive do
  use MyAppWeb, :live_view
  use NavBuddy.LiveHelpers  # Adds event handlers

  def mount(_params, _session, socket) do
    socket =
      socket
      |> assign_navbuddy_state()
      |> assign(:navigation_items, navigation_items())

    {:ok, socket}
  end

  # Event handlers are automatically available:
  # handle_event("navbuddy:item_click", ...)
  # handle_event("navbuddy:dropdown_toggle", ...)
  # handle_event("navbuddy:mobile_toggle", ...)
end

Mobile Optimization

Drawer Mode (Default)

Slide-out navigation drawer on mobile:

%NavBuddy.Config{mobile_behavior: :drawer}

Collapse Mode

Collapsible menu sections:

%NavBuddy.Config{mobile_behavior: :collapse}

Overlay Mode

Full-screen navigation overlay:

%NavBuddy.Config{mobile_behavior: :overlay}

Theming

Automatic Theme Detection

%NavBuddy.Config{theme: :auto}  # Follows system preference

Manual Theme Selection

%NavBuddy.Config{theme: :light}  # or :dark

Custom Themes

Override CSS custom properties:

:root {
  --navbuddy-bg-primary: #your-color;
  --navbuddy-text-primary: #your-color;
  --navbuddy-accent-primary: #your-color;
}

Accessibility Features

NavBuddy is built with accessibility in mind:

Keyboard Navigation

Breadcrumbs

Automatic breadcrumb generation:

<.nav_breadcrumbs
  items={@navigation_items}
  current_path={@current_path}
/>

Advanced Features

Smart Positioning

Automatically adjusts dropdown and mega menu positions to stay within viewport:

%NavBuddy.Config{smart_positioning: true}

Touch Gestures

Mobile-optimized touch interactions:

%NavBuddy.Config{touch_gestures: true}

Analytics Integration

Built-in event tracking:

%NavBuddy.Config{analytics: true}

# Events emitted:
# navbuddy:item_click
# navbuddy:dropdown_open
# navbuddy:dropdown_close
# navbuddy:mobile_toggle

Animation Control

Control animations:

%NavBuddy.Config{animations: true, auto_close_delay: 300}

CSS Classes

NavBuddy provides semantic CSS classes for customization:

JavaScript API

Access the JavaScript API directly:

// Initialize manually
const nav = new NavBuddy(document.querySelector(".navbuddy-nav"), {
  smartPositioning: true,
  keyboardNavigation: true,
  analytics: true,
});

// Methods
nav.openDropdown("products");
nav.closeMegaMenu("solutions");
nav.toggleMobileNav();
nav.closeAllMenus();

// Events
nav.trackEvent("custom_event", { custom: "data" });

// State
const state = nav.getState();

Browser Support

Examples

Check out the included example navigation structure:

NavBuddy.example_navigation()

This returns a comprehensive navigation structure showcasing all features:

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Changelog

v0.1.0 (2025-01-25)

License

MIT License - see LICENSE for details.

Acknowledgments


Made with ❤️ for the Phoenix community