nav-schema
v1.5.2
Published
TypeScript menu schemas for Sidebar and Mega Menu
Maintainers
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-schemayarn
yarn add nav-schemapnpm
pnpm add nav-schemaFor 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-sidebarnav-sidebar-listnav-sidebar-itemnav-sidebar-linknav-sidebar-textnav-sidebar-iconnav-sidebar-arrownav-sidebar-groupnav-sidebar-group-labelnav-sidebar-indicatorcurrent-page-activenav-mega-menunav-mega-menu-listnav-mega-menu-itemnav-mega-menu-linknav-mega-menu-panelnav-mega-menu-columnmega-menu-activemega-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
