npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nav-schema

v1.5.2

Published

TypeScript menu schemas for Sidebar and Mega Menu

Readme

Nav Schema

TypeScript navigation schemas and rendering helpers for building vanilla sidebar menus and three-column mega menus.

Nav Schema gives you a typed data model, HTML renderers, and mount helpers for interactive navigation UI. It is framework-friendly by design: render static HTML, mount directly in the browser, or pass generated bindings into React, Vue, or Angular templates.

Features

  • Typed sidebar and mega-menu schemas
  • Vanilla DOM mounting with cleanup functions
  • Server-safe HTML rendering helpers
  • Current-page matching for path, query, and hash routes
  • Nested sidebar children with optional single-branch expansion
  • Sidebar item and global event handlers
  • Optional floating active-page indicator
  • Hover or click driven mega-menu panels
  • Responsive mega-menu rendering from the same schema array
  • Configurable mobile submenu icons and accordion behavior
  • Animated mega-menu panel and column transitions
  • Custom class-name hooks for styling
  • ESM, CommonJS, browser global, and declaration builds

Install Dependencies

After publishing, install the package from your registry:

npm

npm install nav-schema

yarn

yarn add nav-schema

pnpm

pnpm add nav-schema

For local development in this repository, run pnpm install. This package depends on tiny-engine-core.

If you want to work directly from the source modules, both src/sidebar.ts and src/mega-menu.ts re-export the tiny-engine-core helpers used by the runtime:

import {
  UI,
  getPrefix,
  CapsuleOptions,
  FunctionalCapsuleApi,
  FunctionalCapsuleHooks,
  UIOptions
} from "./src/sidebar";

import {
  UI,
  getPrefix,
  CapsuleOptions,
  FunctionalCapsuleApi,
  FunctionalCapsuleHooks,
  UIOptions
} from "./src/mega-menu";

That makes it easier for plugin authors and framework adapters to build on the same base API without importing from index.ts.

Quick Start

Sidebar

import {
  createSidebarMenu,
  mountSidebar,
} from "nav-schema";

const sidebar = createSidebarMenu({
  title: "Main Navigation",
  sections: [
    {
      id: "main",
      label: "Main",
      items: [
        { id: "home", label: "Home", url: "/" },
        {
          id: "products",
          label: "Products",
          url: "/products",
          children: [
            { id: "new", label: "New Arrivals", url: "/products/new" },
            { id: "sale", label: "On Sale", url: "/products/sale" }
          ]
        }
      ]
    }
  ]
});

mountSidebar(document.querySelector("[nav-sidebar-root]")!, {
  schema: sidebar,
  expandMultiple: false,
  autoExpandActiveChild: true,
  isFloatingIndicator: true
});

Mega menu

import {
  createMegaMenu,
  mountMegaMenu
} from "nav-schema";
const megaMenu = createMegaMenu({
  id: "header-main",
  items: [
    { id: "home", label: "Home", href: "/" },
    {
      id: "products",
      label: "Products",
      href: "/products",
      megaMenu: {
        secondColumn: { id: "categories", title: "Categories" },
        thirdColumn: { id: "resources", title: "Resources" },
        firstColumn: [
          {
            id: "web-suite",
            label: "Web Suite",
            href: "/products/web",
            items: [
              {
                id: "ui-kit",
                label: "UI Kit",
                href: "/products/ui",
                items: [
                  {
                    id: "buttons",
                    label: "Buttons",
                    href: "/products/ui/buttons",
                    description: "Ready button patterns"
                  }
                ]
              }
            ]
          }
        ]
      }
    }
  ]
});

mountMegaMenu(document.querySelector("[nav-mega-root]")!, {
  schema: megaMenu,
  trigger: "hover",
  columnTrigger: "hover",
  isAnimation: true,
  responsive: {
    enabled: true,
    breakpoint: 1024,
    toggleLabel: "Menu",
    closeLabel: "Close",
    submenuClosedIcon: "+",
    submenuOpenIcon: "-",
    expandMultiple: false
  }
});

The same items array powers desktop and mobile. Desktop uses the three-column mega panel, while responsive mode turns each megaMenu.firstColumn tree into a nested mobile menu with expandable levels.

Mega Menu Responsive Options

| Feature | TypeScript | Default | Description | | ------------------- | ---------------------------- | ------------- | ------------------------------------------------------------- | | enabled | enabled?: boolean | false | Turns the mobile mega-menu layout on or off. | | breakpoint | breakpoint?: number | 1024 | Sets the viewport width where the mobile layout activates. | | toggleLabel | toggleLabel?: string | "Menu" | Label shown on the main mobile menu button. | | closeLabel | closeLabel?: string | "Close" | Label shown when the main mobile menu is open. | | menuLabel | menuLabel?: string | "Main menu" | aria-label applied to the mobile navigation wrapper. | | submenuClosedIcon | submenuClosedIcon?: string | "+" | Icon shown on collapsed mobile submenu toggles. | | submenuOpenIcon | submenuOpenIcon?: string | "-" | Icon shown on expanded mobile submenu toggles. | | expandMultiple | expandMultiple?: boolean | true | Allows multiple sibling mobile submenus to stay open at once. |

For a browser-global build, include dist/index.global.js and use window.NavSchema.

Sidebar Schema

Use sections when you need labeled groups. Use items for a single default group.

Sidebar Mount Options

mountSidebar(container, {
  schema,
  prefix: "nav-schema",
  currentUrl: window.location.href,
  expandMultiple: true,
  autoExpandActiveChild: false,
  isFloatingIndicator: false,
  events: {
    onClick: ({ item, e }) => {
      console.log(item.id);
    }
  }
});

The first column is rendered from megaMenu.firstColumn. Selecting a first column node refreshes the second column. Selecting a second column node refreshes the third column.

Mega Menu Mount Options

mountMegaMenu(container, {
  schema,
  trigger: "hover",
  columnTrigger: "hover",
  isAnimation: true
});

trigger controls top-level panel activation. columnTrigger controls first-column and second-column activation. Both accept "hover" or "click". Set responsive: true for the default mobile breakpoint of 1024px, or pass a responsive object to customize labels, submenu icons, breakpoint, and mobile accordion behavior.

Rendering Helpers

Use render helpers when you want HTML without mounting immediately.

import { renderSidebarHtml, renderMegaMenuHtml } from "nav-schema";

const sidebarHtml = renderSidebarHtml(sidebar, {
  currentUrl: "https://example.com/products/new"
});

const megaMenuHtml = renderMegaMenuHtml(megaMenu);

Default Class Names

Common generated classes include:

  • nav-sidebar
  • nav-sidebar-list
  • nav-sidebar-item
  • nav-sidebar-link
  • nav-sidebar-text
  • nav-sidebar-icon
  • nav-sidebar-arrow
  • nav-sidebar-group
  • nav-sidebar-group-label
  • nav-sidebar-indicator
  • current-page-active
  • nav-mega-menu
  • nav-mega-menu-list
  • nav-mega-menu-item
  • nav-mega-menu-link
  • nav-mega-menu-panel
  • nav-mega-menu-column
  • mega-menu-active
  • mega-menu-open

Complete API Reference

Sidebar

| Feature | TypeScript | HTML Example | | ----------------------- | -----------------------------------| ------------------------------------------------------------------ | | title | title: string | "Main Navigation" | | sections | sections: SidebarMenuSection[] | [{ id: "menu", label: "Menu 1", items: [...] }] | | section.id | id: string | "menu" | | section.label | label: string | "Menu 1" | | section.items | items: SidebarMenuItem[] | [{ id: "home", label: "Home", ... }] | | item.id | id: string | "home" | | item.label | label: string | "Home" | | item.url | url: string | "home.html" | | item.icon | icon?: string | "[H]" | | item.onClick | onClick?: (ctx) => void | onClick: ({ e, item }) => { ... } | | item.onMouseLeave | onMouseLeave?: (ctx) => void | onMouseLeave: ({ e, item }) => { ... | | item.onBlur | onBlur?: (ctx) => void | onBlur: ({ e, item }) => { ... } | | item.onFocus | onFocus?: (ctx) => void | onFocus: ({ e, item }) => { ... | | item.onMouseEnter | onMouseEnter?: (ctx) => void | onMouseEnter: ({ item }) => ... | | schema | schema: SidebarMenu | schema: sidebarMenu | | isFloatingIndicator | isFloatingIndicator?: boolean | isFloatingIndicator: true | | expandMultiple | expandMultiple?: boolean | expandMultiple: false | | autoExpandActiveChild | autoExpandActiveChild?: boolean | autoExpandActiveChild: false | | events | events?: SidebarEventHandlers | events: { onClick, onMouseEnter, onMouseLeave, onFocus, onBlur } |

Mega menu

| Feature | TypeScript | Example | | ------------------------ | --------------------------------------------------------- | ---------------------------------------------------- | | id | id: string | "header-main" | | items | items: MegaMenuItem[] | | | item.id | id: string | "" | | item.label | label: string | "" | | item.href | href: string | "/" | | item.icon | icon?: string | "[P]" | | item.dropdownIcon | dropdownIcon?: string | "v" | | item.megaMenu | megaMenu?: { firstColumn; secondColumn?; thirdColumn? } | | | firstColumn | firstColumn: MegaMenuColumnNode[] | | | secondColumn.id | id: string | "categories" | | secondColumn.className | className?: string | "mega-categories" | | secondColumn.title | title?: string | "Categories" | | thirdColumn.id | id: string | "resources" | | thirdColumn.className | className?: string | "mega-resources" | | thirdColumn.title | title?: string | "Resources" | | columnNode.id | id: string | "ui-kit" | | columnNode.label | label: string | "UI Kit" | | columnNode.href | href?: string | "/products/ui" | | columnNode.description | description?: string | "Ready button patterns" | | columnNode.items | items?: MegaMenuColumnNode[] | [{ id: "", label: "", href: "/" }] | | schema | schema: MegaMenuSchema | schema: megaMenu | | trigger | trigger?: "hover" \| "click" | trigger: "hover" | | columnTrigger | columnTrigger?: "hover" \| "click" | columnTrigger: "hover" | | isAnimation | isAnimation?: boolean | isAnimation: true |

Most class names can be replaced through render or mount options such as linkClassName, panelClassName, and columnLinkClassName.

License

MIT (c) 2026 Urvesh Gohil