Admin Shell React block for Tailwind CSS v4
v1.1.0Updated in 1.6.0
A React admin shell layout with a workspace sidebar, a top bar with search and notifications, a command menu and a bottom tab bar on phones.
The sidebar lists the navigation in groups with counts; the active page carries a pill and aria-current, and a record route such as /clients/northwind-tech keeps its section active. The workspace card at the top opens the switcher: picking another workspace spins its row for a moment, switches and confirms with a toast. The account card at the bottom opens the user menu (profile, settings, billing, help, the theme and sign out). Press mod+b or the toggle in the sidebar footer to collapse it to an icon rail with tooltips and flyouts, drag its edge to resize it (double click resets) or use the arrow keys on the handle. The top bar shows the page title or a breadcrumb, the search field (press / to focus it; results for clients, contacts, deals and invoices appear as you type, arrows and Enter open one), the notification bell (tabs for all, unread and mentions, mark as read, swipe to dismiss) and the theme toggle. mod+k opens the command menu: go to any page, create a client, invoice or deal, invite a colleague, switch the theme or toggle the sidebar. Below 1024px the sidebar becomes a drawer behind the menu button, the top bar turns compact with a centered title, a search icon that opens a full screen search, the bell (a sheet) and your avatar, and a five tab bar sits on the bottom edge above the safe area; its More tab opens the drawer and the top bar slides away while you scroll down. The skip link (Skip to content) is the first stop for keyboard users. design="rail" keeps the sidebar on its icon rail (hover or focus an icon for its name); design="topbar" moves the nav into the top bar as tabs, where the arrow keys move between tabs and the More menu holds the ones that do not fit.
Admin Shell is a Pro React block in the React blocks category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add admin-shell, together with App Shell, Badge, Bottom Tab Bar, Breadcrumb, Button and 16 more and the npm package lucide-react.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add admin-shellAdmin Shell is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import AdminShell from @/components/blocks/admin-shell.
// app/(admin)/layout.tsx
"use client"
import Link from "next/link"
import { usePathname, useRouter } from "next/navigation"
import type { ReactNode } from "react"
import { AdminShell } from "@/components/blocks/admin-shell"
export default function AdminLayout({ children }: { children: ReactNode }) {
const pathname = usePathname()
const router = useRouter()
return (
<AdminShell currentPath={pathname} linkComponent={Link} onNavigate={router.push}>
{children}
</AdminShell>
)
}Props
childrenReactNodeThe page, rendered once inside main (never twice for phone and desktop).
navSidebarNavGroup[]The sidebar groups (ADMIN_SHELL_SAMPLE.nav: Home, AI assistant; Sales with Clients 128, Contacts, Deals 12, Orders; Finance with Invoices 3 overdue, Reports; Operations; Settings and Help). Also feeds the command menu's Go to group.
tabsBottomTabBarItem[]The phone tab bar, 3 to 5 items (ADMIN_SHELL_SAMPLE.tabs: Home, Clients, Deals, Invoices, More). An item with id "more" and no href opens the drawer. [] hides the bar.
currentPathdefault"/"stringThe active route (next: usePathname()). Selects the nav item, the tab and the default title; nested routes match by prefix.
titledefault""ReactNodeThe top bar title. Empty uses the active nav item's label, or labels.appName when no item matches.
breadcrumbBreadcrumbEntry[]Replaces the title with a breadcrumb (record pages: Clients / Northwind Tech Inc.). Phones show the last entry as the title.
actionsReactNodeExtra top bar actions before the notifications, such as a New button.
bannerReactNodeA strip above everything, usually a Banner (trial ending, maintenance). Hidden when printing.
workspacesWorkspace[]The switcher's list (ADMIN_SHELL_SAMPLE.workspaces: Summit Software Inc., Summit Logistics, Personal). One workspace hides the chevron; none shows the logo instead.
workspacestringThe active workspace id, controlled. Pair it with onWorkspaceChange.
defaultWorkspacedefault"summit-software"stringThe workspace selected at first, uncontrolled.
onWorkspaceChange(id: string, workspace: Workspace) => (void | Promise<unknown>)Runs when a workspace is picked. A returned promise keeps the row spinning; once it resolves the switch happens and a toast confirms it, a rejection keeps the old workspace and shows an error toast. Without it a 700 ms request is simulated.
user{ name: string; email: string; avatar?: string; plan?: string } | nullThe signed-in person for the sidebar card and the phone avatar menu (ADMIN_SHELL_SAMPLE.user: Anna Price, Administrator). null shows a sign-in button.
userMenuItemsMenuEntry[]The account menu entries above sign out. Default: Profile, Settings, Billing, Help and a separator, navigating through the router.
onSignOut() => (void | Promise<unknown>)The sign-out row; a promise keeps the menu open with a spinner and a rejection shows an error toast. Default: a toast "You signed out".
signInHrefdefault"/sign-in"stringWhere the sign-in button points while there is no user.
notificationsNotificationItem[]The bell's inbox (ADMIN_SHELL_SAMPLE.notifications: an overdue invoice, a mention, a payment, a won deal, an import and an accepted invite; 3 unread). Read, unread and dismiss are handled by the notification center.
onNotificationSelect(item: NotificationItem) => voidOpening a notification. Default: it turns read and navigates to its href through the router.
searchdefaulttruebooleanThe global search in the top bar (an icon that opens a full screen search on phones).
searchResultsSearchResultGroup[]Static results filtered as you type (ADMIN_SHELL_SAMPLE.searchResults: clients, contacts, deals and invoices, grouped).
loadSearch(query: string, context: { signal: AbortSignal; scope: string }) => Promise<SearchResultGroup[]>Server search instead of searchResults: debounced, the previous request aborted.
searchSuggestionsSearchResult[]Quick links while the search is empty (Overdue invoices, Deals in negotiation, Clients in Chicago).
searchShortcutdefault"/"stringFocuses the search (opens it on phones). "" binds nothing.
commandMenudefaulttruebooleanThe command palette: every nav page, the actions (client, invoice, deal, invite), the theme and the sidebar toggle.
commandsCommandGroup[]The palette's root groups. Default: Go to (built from nav) and Actions (ADMIN_SHELL_SAMPLE.commands). The Appearance group is always added.
commandShortcutdefault"mod+k"stringOpens the command menu. "" binds nothing.
sidebarShortcutdefault"mod+b"stringToggles the sidebar: the rail on desktop (classic design), the drawer on phones. "" binds nothing.
designdefault"classic""classic" | "rail" | "topbar"classic: the sidebar with workspaces and the account card. rail: a fixed 64px icon rail with tooltips and flyouts, the workspace logo on top and the avatar at the bottom. topbar: no sidebar on desktop; the logo, the nav as tabs (what does not fit goes under More), search, notifications and the avatar share the top bar. Phones keep the drawer and the tab bar in every design.
layoutdefault"page""page" | "fill"page: the window scrolls under a sticky top bar. fill: the shell is exactly the viewport, the top bar stays put and main is a flex column for boards and inboxes that scroll inside.
variantdefault"inset""sidebar" | "inset" | "floating"inset: the page is a raised panel beside the sidebar. sidebar: flush columns with a hairline. floating: the sidebar is a raised panel off the edges. Applies to the classic design.
defaultCollapseddefaultfalsebooleanStarts as the icon rail on desktop. Applies to the classic design.
collapsibledefault"icon""icon" | "offcanvas" | "none"icon: collapses to a 64px rail. offcanvas: slides out completely. none: always expanded. Applies to the classic design.
persistKeydefault"admin-shell"stringRemembers collapsed and width in cookies (readable by a server layout) and the search and palette recents. "" turns it off; the demo passes "".
resizabledefaulttruebooleanDrag the sidebar edge between 200 and 400px; below 120px it snaps to the rail. Applies to the classic design.
mobileBreakpointdefault"lg""md" | "lg"Below it: the drawer, the compact top bar and the tab bar. lg gives an 820px tablet the full width for the page.
sidebarActiveStyledefault"soft""soft" | "raised" | "bar"How the sidebar marks the active page: a soft pill, a raised pill or a bar.
hideTabBardefaultfalsebooleanHides the phone tab bar (pages with their own bottom bar, thumbnails).
logoReactNodeShown in the sidebar header when there are no workspaces. Default: a 28px tile with an "S" and Summit CRM.
localedefault"en-US"stringIntl locale for counts and times in the search and the notifications.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"The accent of the active nav item, the active tab and the unread dots.
linkComponent(props: LinkComponentProps) => ReactNodeThe router link every nav link renders through (next/link). Default: the LinkProvider's, else <a>.
onNavigate(href: string) => voidThe router's push for search results, commands and menu items picked without an anchor (next: useRouter().push).
labelsPartial<AdminShellLabels>Every visible string, English by default (DEFAULT_ADMIN_SHELL_LABELS; ADMIN_SHELL_LABELS_EN is the same set). Nested sets translate the composed items.
classNamesPartial<Record<"sidebar" | "topbar" | "main" | "tabs", string>>Extra classes for the sidebar, the top bar, main and the tab bar row.
classNamestringExtra classes for the root. Other props spread onto it.
Parts
ADMIN_SHELL_DESIGNS
constantThe design names, classic first: ["classic", "rail", "topbar"].
AdminShellDesign
typeThe layout design: "classic" | "rail" | "topbar".
DEFAULT_ADMIN_SHELL_LABELS
constantThe English strings for every visible label, the default of labels. Type: AdminShellLabels.
ADMIN_SHELL_LABELS_EN
constantThe same English strings under their older name, kept for existing imports.
ADMIN_SHELL_SAMPLE
constantThe Summit CRM demo data: nav, tabs, workspaces, user, notifications, searchResults, searchSuggestions and commands.
ADMIN_SHELL_NOW
constantThe demo clock (Sep 24, 2026, 4:40 PM) as a timestamp, so notification times stay stable.
AdminShellProps
typeProps of AdminShell, including the div attributes of the root.
AdminShellUser
typeThe signed-in person: { name, email, avatar?, plan? }.
Examples
Rail design
design="rail": a fixed 64px icon rail with tooltips, the workspace logo on top and your avatar at the bottom; phones keep the drawer.
Topbar design
design="topbar": no sidebar on desktop; the logo, the nav as tabs (the rest under More), search, notifications and your avatar share the top bar.
Collapsed rail with a trial banner
defaultCollapsed starts on the icon rail (tooltips and flyouts); banner sits above everything.
A record route with a breadcrumb
currentPath="/clients/northwind-tech" keeps Clients active by prefix; breadcrumb replaces the title.
Fill layout with a board
layout="fill": the shell is the viewport and the pipeline scrolls sideways inside the page.
Floating sidebar, brand tone
variant="floating", tone="brand" and the raised active pill.
en-GB with a custom user menu
locale="en-GB" and userMenuItems replacing the default Profile, Settings, Billing and Help entries.
Signed out, no workspaces
user={null} shows a sign-in button in the sidebar; no workspaces shows the logo.
Dependencies
Files
- components/blocks/admin-shell.tsx
- components/blocks/admin-shell-data.ts
FAQ
Is the React Admin Shell block free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Admin Shell?
Run npx wingo-ui@latest add admin-shell. It also adds the Wingo UI items it builds on (App Shell, Badge, Bottom Tab Bar, Breadcrumb, Button and 16 more) and the npm package lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/admin-shell.
See also
More React blocks
Articles using this component
- Shadcn Sidebar Mobile: Fix the Sheet and Add a Tab Bar
Mobile UI in React9 min read