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

@rqdhw3n/react-admin-layout

v1.0.3

Published

Production-ready admin dashboard layout for React — sidebar, navbar, dark mode, breadcrumbs, RBAC

Readme

@rqdhw3n/react-admin-layout

Production-ready admin dashboard layout for React — sidebar, navbar, dark mode, breadcrumbs, RBAC, mobile drawer.

React TypeScript

Features

  • Responsive collapsible sidebar
  • Mobile drawer menu
  • Top navbar with search, notifications, user menu
  • Breadcrumbs + auto-generation from route path
  • Dark mode with localStorage persistence
  • Light, dark, and live system mode; custom palettes; modern, minimal, and compact styles
  • RBAC permission filtering on sidebar items
  • React Router support (optional peer)
  • Tailwind CSS — modern SaaS aesthetic
  • Full TypeScript

Install

npm install @rqdhw3n/react-admin-layout lucide-react
# optional for routing
npm install react-router-dom

Styles (required)

import '@rqdhw3n/react-admin-layout/style.css'

Works with Vite, Next.js (app/layout.tsx or _app.tsx), and CRA.

Next.js

// app/layout.tsx
import '@rqdhw3n/react-admin-layout/style.css'

Tailwind in your app

If you use Tailwind v3+, add the package to content:

content: [
  './src/**/*.{js,ts,jsx,tsx}',
  './node_modules/@rqdhw3n/react-admin-layout/dist/**/*.js',
],

Basic usage

import { AdminLayout, type SidebarItemType } from '@rqdhw3n/react-admin-layout'
import '@rqdhw3n/react-admin-layout/style.css'
import { Home, Users } from 'lucide-react'

const sidebarItems: SidebarItemType[] = [
  { label: 'Dashboard', icon: Home, path: '/dashboard' },
  {
    label: 'Users',
    icon: Users,
    children: [
      { label: 'List', path: '/users' },
      { label: 'Create', path: '/users/create', permission: 'users.create' },
    ],
  },
]

export default function App() {
  return (
    <AdminLayout
      sidebarItems={sidebarItems}
      appName='My Admin'
      user={{ name: 'Amine', email: '[email protected]' }}
      permissions={{ users: ['view', 'create'] }}
      activePath='/dashboard'
    >
      <YourPage />
    </AdminLayout>
  )
}

Theming

Navigation position and separated panels

The layout supports three independent navigation positions and two panel arrangements:

<AdminLayout
  sidebarItems={sidebarItems}
  sidebarPosition="left" // "left" | "right" | "top"
  layoutVariant="detached" // "default" | "detached"
  layoutGap={12}
  theme={{ mode: 'dark', style: 'modern' }}
>
  <YourPage />
</AdminLayout>
  • sidebarPosition="left" is the existing default; "right" places the sidebar on the right.
  • "top" renders horizontal navigation above the navbar, with click/keyboard disclosure panels for nested links. Escape and clicking outside close them. The navigation wraps on narrower desktop screens. Sidebar collapse is hidden in this position; its saved state is retained when switching back to a vertical sidebar.
  • layoutVariant="default" keeps the original joined layout. "detached" gives the sidebar and the main panel (navbar + content) rounded edges, an outer margin, and a visible gap between them.
  • layoutGap is a nonnegative pixel value (default 12), used only for detached layouts. The actual gap is capped at 5vw to retain usable space on small screens. Invalid nonfinite values use 12.
  • Below 1024px, all positions use the mobile drawer. The right layout opens the drawer on the right; left and top layouts open it on the left.

All options update immediately when their props change and work with every theme mode and style. They do not alter the stored theme or collapse preference. The exported types are AdminLayoutSidebarPosition and AdminLayoutVariant. For example:

const [position, setPosition] = useState<AdminLayoutSidebarPosition>('left')
// Pass sidebarPosition={position}; call setPosition('right') or setPosition('top').

To reproduce a dark layout with a contrasting frame like the reference:

theme={{
  mode: 'dark',
  style: 'modern',
  colors: {
    background: '#1e293b', // Visible outer frame and gap
    surface: '#020617',
    sidebarBackground: '#020617',
    headerBackground: '#020617',
  },
}}

The root exposes data-sidebar-position and data-layout-variant. --ral-layout-gap is the CSS gap token; nested layouts reset their own positioning and panel geometry. The demo includes position, arrangement and spacing controls.

The optional theme prop adds custom colors and three visual styles to the existing layout. Keep using sidebarItems, appName, and the other existing props. Import the stylesheet once:

import {
  AdminLayout,
  type AdminLayoutTheme,
  type AdminLayoutThemeMode,
  type AdminLayoutStyle,
} from '@rqdhw3n/react-admin-layout'
import '@rqdhw3n/react-admin-layout/styles.css'

The original @rqdhw3n/react-admin-layout/style.css import remains supported and points to the same file. Consumers do not need Tailwind installed to use the shipped styles. Tailwind configuration is only needed when generating additional utility classes in your own app.

Basic dark mode

<AdminLayout sidebarItems={sidebarItems} theme={{ mode: 'dark' }}>
  <YourPage />
</AdminLayout>

Built-in style

<AdminLayout sidebarItems={sidebarItems} theme={{ style: 'compact' }}>
  <YourPage />
</AdminLayout>

Every style supports every mode and palette:

| Style | Sidebar / collapsed | Header | Navigation height | Content spacing | Radius | Appearance | | --- | --- | --- | --- | --- | --- | --- | | modern (default) | 260 / 72 px | 64 px | 44 px | 24 px | 10 px | Rounded navigation and cards, subtle shadows | | minimal | 260 / 72 px | 64 px | 46 px | 32 px | 4 px | Flat surfaces, borders, extra whitespace | | compact | 220 / 60 px | 52 px | 34 px | 16 px | 6 px | Dense navigation and content, smaller header |

adminLayoutPresets.modern, .minimal, and .compact are immutable, optional convenience objects:

import { adminLayoutPresets } from '@rqdhw3n/react-admin-layout'

<AdminLayout sidebarItems={sidebarItems} theme={{
  ...adminLayoutPresets.modern,
  colors: { primary: '#15803d', activeText: '#ffffff', onPrimary: '#ffffff' },
}}>
  <YourPage />
</AdminLayout>

Custom colors

<AdminLayout sidebarItems={sidebarItems} theme={{
  colors: {
    primary: '#7c3aed',
    activeText: '#ffffff',
    onPrimary: '#ffffff',
    sidebarBackground: '#111827',
    sidebarText: '#cbd5e1',
  },
}}>
  <YourPage />
</AdminLayout>

All colors accept CSS color values, including CSS variables. Default text/foreground pairs meet 4.5:1 contrast. When providing custom backgrounds, choose readable matching foregrounds: sidebarText, headerText, hoverText, activeText, onPrimary, and onDanger. The library does not infer contrast for arbitrary CSS colors.

Custom light/dark

<AdminLayout sidebarItems={sidebarItems} theme={{
  mode: 'system',
  light: { primary: '#2563eb' },
  dark: { primary: '#60a5fa', activeText: '#0f172a', onPrimary: '#0f172a' },
}}>
  <YourPage />
</AdminLayout>

Resolution order is selected built-in light/dark palette → colors → selected light or dark overrides. Missing, undefined, and empty color values retain the earlier value. An overridden primary also sets activeBackground unless an explicit activeBackground override exists. Other color slots remain independent. Neither the input configuration nor the built-in defaults are mutated.

Runtime switching and the existing toggle

import { useState } from 'react'
import { AdminLayout, type AdminLayoutThemeMode } from '@rqdhw3n/react-admin-layout'

function App() {
  const [mode, setMode] = useState<AdminLayoutThemeMode>('system')
  return (
    <AdminLayout
      sidebarItems={sidebarItems}
      theme={{ mode, style: 'modern' }}
      onThemeModeChange={setMode}
    >
      <YourPage />
    </AdminLayout>
  )
}

An explicit theme.mode is controlled: it always wins over storage and initialDarkMode. Prop changes apply immediately. Connect onThemeModeChange to update parent state when the built-in ThemeToggle, useDarkMode(), or new hook requests a mode. Without that callback, the controlled mode stays fixed. Toggling from system selects the opposite of the effective mode.

When theme.mode is omitted, the layout manages and persists the mode internally under the existing storageKey. Existing stored darkMode values and initialDarkMode still work; the saved state now also supports mode: 'system'. Controlled modes do not overwrite the saved uncontrolled preference. Use distinct storage keys for independent layout preferences.

import { useAdminLayoutTheme } from '@rqdhw3n/react-admin-layout'

function FollowSystemButton() {
  const { mode, resolvedMode, style, colors, setMode } = useAdminLayoutTheme()
  return <button onClick={() => setMode('system')}>Follow system ({resolvedMode})</button>
}

Render this hook beneath AdminLayout or LayoutProvider. mode is the requested mode; resolvedMode is always light or dark. The existing useDarkMode() API is unchanged. System mode subscribes to prefers-color-scheme changes and cleans up its listener. Server rendering uses light for system mode; hydration then reads the OS preference and stored state. Blocked storage and unavailable matchMedia are handled safely.

Public TypeScript API

export type AdminLayoutThemeMode = 'light' | 'dark' | 'system'
export type AdminLayoutResolvedMode = 'light' | 'dark'
export type AdminLayoutStyle = 'modern' | 'minimal' | 'compact'

export interface AdminLayoutColors {
  primary?: string
  secondary?: string
  background?: string
  surface?: string
  text?: string
  textSecondary?: string
  sidebarBackground?: string
  sidebarText?: string
  headerBackground?: string
  headerText?: string
  hoverBackground?: string
  hoverText?: string
  activeBackground?: string
  activeText?: string
  border?: string
  onPrimary?: string
  danger?: string
  onDanger?: string
  overlay?: string
}

export interface AdminLayoutTheme {
  mode?: AdminLayoutThemeMode
  style?: AdminLayoutStyle
  colors?: AdminLayoutColors
  light?: AdminLayoutColors
  dark?: AdminLayoutColors
}

AdminLayoutProps and LayoutProviderProps add theme?: AdminLayoutTheme and onThemeModeChange?: (mode: AdminLayoutThemeMode) => void. All existing props remain. Also exported: ResolvedAdminLayoutTheme, lightThemeColors, darkThemeColors, adminLayoutPresets, resolveTheme(theme?, systemMode?), and themeToCSSVariables(resolvedTheme). The pure resolver defaults its system-mode argument to light; automatic OS tracking belongs to the provider.

CSS variables and scope

The root exposes .ral-layout.admin-layout-root, data-theme="light|dark", and data-style="modern|minimal|compact". Palette variables are applied once at that boundary. Layouts with an explicit theme have independent colors and density, including nested layouts. The .dark class is also present on dark layout roots for existing descendant Tailwind utilities. For backward compatibility, only layouts without any theme prop still synchronize the document's .dark class. Use theme={{}} to opt into scoped theming with an uncontrolled toggle.

| Palette fields | CSS variables | | --- | --- | | primary, secondary | --ral-primary, --ral-secondary | | background, surface | --ral-background, --ral-surface | | text, textSecondary | --ral-text, --ral-text-secondary | | sidebarBackground, sidebarText | --ral-sidebar-bg, --ral-sidebar-text | | headerBackground, headerText | --ral-header-bg, --ral-header-text | | hoverBackground, hoverText | --ral-hover-bg, --ral-hover-text | | activeBackground, activeText | --ral-active-bg, --ral-active-text | | border, onPrimary | --ral-border, --ral-on-primary | | danger, onDanger, overlay | --ral-danger, --ral-on-danger, --ral-overlay |

Density tokens include --ral-sidebar-width, --ral-sidebar-collapsed, --ral-header-height, --ral-radius, --ral-spacing, --ral-nav-item-height, and --ral-shadow. Existing --admin-* variables remain as aliases. The optional .ral-card class gives consumer cards the selected surface, border, radius, padding, and shadow. Other consumer components must use these variables or their own theme integration; arbitrary child markup is not restyled.

.ral-layout.my-layout { --ral-sidebar-width: 280px; }
.my-card { background: var(--ral-surface); color: var(--ral-text); border: 1px solid var(--ral-border); }

Use className="my-layout" on AdminLayout and load your overrides after the package CSS. Transitions respect prefers-reduced-motion. Keyboard focus remains visible, nested navigation works when collapsed, user menu supports arrow keys/Escape, and the mobile drawer traps and restores focus.

Demo and verification

Development scripts require Node.js 22.18+ (native TypeScript stripping for the CSS generator). This is a contributor requirement; the published package contains compiled JavaScript.

npm install
npm run build
npm run demo

Open http://127.0.0.1:4173. The demo includes light/dark/system, modern/minimal/compact, default/indigo/blue/green/rose/orange palettes, separate mode palettes, and custom sidebar/header colors. It consumes public package exports. /?legacy exercises the old uncontrolled API; /?isolation demonstrates independent nested layouts.

npm run typecheck
npm test
npm run verify:package
npx playwright install chromium
npm run test:browser

verify:package packs the local build, installs that tarball into an isolated Vite app under .tmp, checks every export target, compiles ESM/CommonJS type consumers, and builds the demo. The browser suite then runs that installed app, including screenshots in test-results. It tests with React 18 and React Router 6; the library's development build uses Router 7. On Windows, an existing Edge installation can be used instead of downloading Chromium:

$env:PLAYWRIGHT_CHANNEL = 'msedge'
npm.cmd run test:browser

Publishing (manual)

No automatic publishing is configured. For the next minor release from the existing 1.0.1:

npm version minor --no-git-tag-version
npm install
npm run build
npm run typecheck
npm test
npm run verify:package
npm run test:browser
npm pack --dry-run
npm login
npm publish --access public

Use npm.cmd on Windows if PowerShell blocks npm.ps1. The package name remains @rqdhw3n/react-admin-layout; the version is left unchanged until you run the release commands.

React Router

import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { AdminLayout, RouterLink } from '@rqdhw3n/react-admin-layout'

function App() {
  return (
    <BrowserRouter>
      <AdminLayout
        enableRouter
        LinkComponent={RouterLink}
        sidebarItems={items}
        appName='My Admin'
      >
        <Routes>
          <Route path='/dashboard' element={<Dashboard />} />
        </Routes>
      </AdminLayout>
    </BrowserRouter>
  )
}

Hooks

| Hook | Description | |------|-------------| | useSidebar() | Collapsed state, mobile drawer | | useDarkMode() | Theme toggle | | useAdminLayoutTheme() | Requested/effective mode, palette, style, mode setter | | useLayoutState() | Full layout context | | useBreadcrumbs() | Breadcrumb items |

Components

AdminLayout · Sidebar · SidebarItem · SidebarGroup · Navbar · NavbarSearch · UserMenu · NotificationMenu · Breadcrumbs · Footer · MobileSidebar · ThemeToggle · RouterLink

RBAC

permissions={{ users: ['view', 'create'], roles: ['view'] }}

// sidebar item
{ label: 'Create', path: '/users/create', permission: 'users.create' }

Examples

Build (library)

npm install
npm run build

Outputs:

  • dist/index.js (ESM)
  • dist/index.cjs (CJS)
  • dist/index.d.ts
  • dist/index.d.cts (CommonJS declarations)
  • dist/style.css

Peer dependencies

  • react >= 18
  • react-dom >= 18
  • lucide-react >= 0.400
  • react-router-dom >= 6 (optional)

License

MIT