Templates
Dashboard template
A dashboard: an app shell with a brand, a navigation and a user menu fixed, and a page header for each page.
A template renders AppShellLayout, fixes part of it, and exposes props for the rest. It is copy-code: the file is yours to edit.
Preview
Anatomy names each slot and what fills it. The panel changes the props live; the fixed parts stay.
"use client"import type { ReactNode } from "react"import { AppShellLayout } from "@glob-ui/app-shell-layout"import { LayoutNavTrigger } from "@glob-ui/layout"import { PageLayout, PageHeader } from "@glob-ui/page-layout"import { buttonVariants } from "@glob-ui/button"import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator } from "@glob-ui/dropdown-menu"// Client: the user menu passes handlers (onSelect), which a Server Component cannot pass to a client component.// Edit: this is yours. The brand, the navigation and the user menu are fixed for every page of the dashboard.const BRAND = { name: "Acme", href: "/" }const NAV_ITEMS = [ { label: "Overview", href: "/" }, { label: "Orders", href: "/orders" }, { label: "Customers", href: "/customers" }, { label: "Settings", href: "/settings" },]const USER = { name: "Ada Lovelace", email: "ada@acme.test", initials: "AL" }export interface DashboardTemplateProps { /** The page title (an h1). */ title: ReactNode description?: ReactNode breadcrumbs?: ReactNode /** Buttons for the page, next to the title. */ actions?: ReactNode /** The href of the current page. Its link gets `aria-current="page"`. */ activeHref?: string children?: ReactNode}/** A dashboard: the app shell with a fixed brand, navigation and user menu, and a page header for each page. */export function DashboardTemplate({ title, description, breadcrumbs, actions, activeHref, children }: DashboardTemplateProps) { return ( <AppShellLayout sidebarLabel="Main navigation" header={ <> <LayoutNavTrigger /> <div data-template-part="fixed" data-template-name="BRAND, user menu" className="flex min-w-0 flex-1 items-center justify-between gap-4"> <a href={BRAND.href} className="truncate font-semibold text-glob-text no-underline"> {BRAND.name} </a> <DropdownMenu> <DropdownMenuTrigger className={buttonVariants({ variant: "ghost", size: "sm" })}> {USER.initials} <span className="sr-only">Account of {USER.name}</span> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>{USER.email}</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuItem onSelect={() => undefined}>Profile</DropdownMenuItem> <DropdownMenuItem onSelect={() => undefined}>Sign out</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </div> </> } sidebar={ <div data-template-part="fixed" data-template-name="navigation" className="flex flex-col gap-1"> {NAV_ITEMS.map((item) => ( <a key={item.href} href={item.href} aria-current={item.href === activeHref ? "page" : undefined} className="rounded-glob-md px-3 py-2 text-sm text-glob-text no-underline hover:bg-glob-surface-hover aria-[current=page]:bg-glob-surface-hover aria-[current=page]:font-medium" > {item.label} </a> ))} </div> } > <PageLayout header={ <div data-template-part="prop" data-template-name="title, description, breadcrumbs, actions"> <PageHeader title={title} description={description} breadcrumbs={breadcrumbs} actions={actions} /> </div> } > <div data-template-part="prop" data-template-name="children" className="flex min-w-0 flex-col gap-6"> {children} </div> </PageLayout> </AppShellLayout> )}How it uses the layout
The template fills these slots of AppShellLayout. Each slot is fixed by the template, filled by a prop, or not used.
| Layout slot | Filled with | Fixed or prop |
|---|---|---|
AppShellLayout.header | brand, user menu, navigation trigger on small screens | fixed |
AppShellLayout.sidebar | navigation, with aria-current on the active link | fixed |
AppShellLayout.sidebar | activeHref | prop |
AppShellLayout.main | a PageLayout (page structure) | fixed |
PageLayout.header (in AppShellLayout.main) | title, description, breadcrumbs, actions | prop |
PageLayout.content (in AppShellLayout.main) | children | prop |
PageLayout.aside (in AppShellLayout.main) | not used | – |
Fixed and variable
Fixed by the template (edit the constants at the top of the file):
- Brand
- Navigation with
aria-current - User menu
- Navigation trigger on small screens
Props (the variable part):
title,description,breadcrumbs,actionsactiveHref: the current pagechildren: the page content
Install
npx shadcn@latest add https://globui.dev/r/dashboard-template.jsonCode
The template is one file, and a page uses it. The page sets only what varies: no heights, no overflow.