@mshafiqyajid/react-navbar
Headless navbar hook and styled component for React. Sticky positioning, transparent-on-top with scroll detection, collapsible mobile menu with hamburger toggle, four variants, three sizes, full keyboard navigation, and ARIA-compliant.
Playground #
Props
TSX
import { NavbarStyled } from "@mshafiqyajid/react-navbar/styled";
import "@mshafiqyajid/react-navbar/styles.css";
<NavbarStyled
brand={brand}
items={navItems}
actions={actions}
/>Install #
npm install @mshafiqyajid/react-navbar Quick start #
import { NavbarStyled } from "@mshafiqyajid/react-navbar/styled";
import "@mshafiqyajid/react-navbar/styles.css";
const navItems = [
{ label: "Home", href: "/", active: true },
{ label: "Docs", href: "/docs" },
{ label: "Pricing", href: "/pricing" },
];
<NavbarStyled
brand={<strong>Acme</strong>}
items={navItems}
actions={<button>Sign in</button>}
/> Sticky + transparent on top #
Set sticky to keep the navbar fixed at the top of the viewport. Pair with transparentOnTop to start transparent and transition to a solid background once the user scrolls past the threshold.
<NavbarStyled
brand={<strong>Acme</strong>}
items={navItems}
sticky
transparentOnTop
scrollThreshold={16}
/> Variants #
<NavbarStyled items={navItems} variant="default" /> {/* subtle bottom border */}
<NavbarStyled items={navItems} variant="bordered" /> {/* border + shadow */}
<NavbarStyled items={navItems} variant="filled" /> {/* dark filled background */}
<NavbarStyled items={navItems} variant="transparent" />{/* fully transparent */} Sizes #
<NavbarStyled items={navItems} size="sm" /> {/* 48px height */}
<NavbarStyled items={navItems} size="md" /> {/* 56px — default */}
<NavbarStyled items={navItems} size="lg" /> {/* 64px height */} Nav items with icons #
const navItems = [
{ label: "Home", href: "/", active: true, icon: <HomeIcon /> },
{ label: "Docs", href: "/docs", icon: <BookIcon /> },
{ label: "Admin", href: "/admin", disabled: true },
];
<NavbarStyled brand={<strong>Acme</strong>} items={navItems} /> Mobile menu toggle #
A hamburger button appears automatically below the mobileBreakpoint (default 768 px). Clicking it toggles a slide-down menu. Pressing Escape closes it.
<NavbarStyled
brand={<strong>Acme</strong>}
items={navItems}
actions={<button>Sign in</button>}
mobileBreakpoint={768}
onMenuToggle={(open) => console.log("menu:", open)}
/> Headless hook #
import { useNavbar } from "@mshafiqyajid/react-navbar";
function MyNavbar() {
const {
navProps,
menuProps,
toggleProps,
isMenuOpen,
isScrolled,
} = useNavbar({
scrollThreshold: 16,
onMenuToggle: (open) => console.log("menu:", open),
});
return (
<nav {...navProps} style={{ background: isScrolled ? "#fff" : "transparent" }}>
<div style={{ display: "flex", alignItems: "center", padding: "0 1rem" }}>
<strong>Acme</strong>
<button {...toggleProps} aria-label="Toggle menu">☰</button>
</div>
<div {...menuProps} hidden={!isMenuOpen}>
<a href="/docs">Docs</a>
<a href="/pricing">Pricing</a>
</div>
</nav>
);
} API — NavbarStyled #
| Prop | Type | Default | Description |
|---|---|---|---|
| brand | ReactNode | — | Brand logo or wordmark rendered on the left |
| items | NavbarItem[] | — | Navigation links (see NavbarItem below) |
| actions | ReactNode | — | Right-side content (buttons, avatar, etc.) |
| variant | "default" | "bordered" | "filled" | "transparent" | "default" | Visual style |
| size | "sm" | "md" | "lg" | "md" | Navbar height (48 / 56 / 64 px) |
| sticky | boolean | false | Stick to the top of the viewport while scrolling |
| transparentOnTop | boolean | false | Use transparent background when page is at the top |
| scrollThreshold | number | 16 | Pixels scrolled before isScrolled becomes true |
| mobileBreakpoint | number | 768 | Viewport width in px below which the mobile menu appears |
| onMenuToggle | (open: boolean) => void | — | Called when the mobile menu opens or closes |
| children | ReactNode | — | Extra content appended inside the nav bar row |
| className | string | — | Extra class on the root <nav> |
| style | CSSProperties | — | Inline style on the root <nav> |
| ref | Ref<HTMLElement> | — | Forwarded to the <nav> element |
API — NavbarItem #
| Key | Type | Description |
|---|---|---|
| label | string | Link text (required) |
| href | string | Link target (required) |
| active | boolean | Mark as current page (aria-current="page") |
| disabled | boolean | Prevent interaction (aria-disabled="true") |
| icon | ReactNode | Optional icon rendered before the label |
API — useNavbar #
| Key | Type | Description |
|---|---|---|
| navProps | object | Spread on <nav>: role="navigation", aria-label |
| menuProps | object | Spread on the mobile menu container: id="rnav-menu" |
| toggleProps | object | Spread on the hamburger button: aria-expanded, aria-controls, onClick |
| isMenuOpen | boolean | Whether the mobile menu is currently open |
| isScrolled | boolean | Whether the page has scrolled past the threshold |
Data attributes #
| Attribute | Values | Description |
|---|---|---|
| data-variant | "default" | "bordered" | "filled" | "transparent" | Current variant (or "transparent" when transparentOnTop + at top) |
| data-size | "sm" | "md" | "lg" | Current size |
| data-sticky | present / absent | Present when sticky is true |
| data-scrolled | present / absent | Present when page is scrolled past threshold |
| data-menu-open | present / absent | Present when mobile menu is open |
Keyboard support #
| Key | Behaviour |
|---|---|
| Tab | Standard focus order through all interactive elements |
| Escape | Closes the mobile menu when it is open |