Application patterns
Shells, page headers, forms, search, transfer lists and command palettes.
Quick start
After the application setup, copy this component into your application. It imports only public packages; the complete live demo later on this page also uses documentation layout helpers.
'use client';
import { useState } from 'react';
import { NavigationDrawer } from '@fayegram-ai/faye-ui/patterns/navigation-drawer';
import { PageHeader } from '@fayegram-ai/faye-ui/patterns/content';
export function ExploreLayout() {
const [open, setOpen] = useState(true);
return <NavigationDrawer open={open} onOpenChange={setOpen} title="Explore"
navigation={<nav aria-label="Places"><a href="/places">Places</a></nav>}>
<main><PageHeader title="Your next small adventure" description="Choose a place to begin." /></main>
</NavigationDrawer>;
}API reference: Navigation Drawer, Content.
Usage contract
Patterns use public primitives and keep business logic outside the library. AppShell supports a configurable main element for embedded previews so documents do not accidentally contain nested main landmarks. PageHeader exposes heading level independently from visual size.
CommandPalette filters supplied labels and keywords, preserves disabled commands, and delegates keyboard selection and focus to Combobox/Dialog. Its search clears on selection, dismissal, and a parent changing open to false, so reopening starts with the full command list. Global shortcuts and remote request ordering remain application-owned.
Customization
Use semantic tokens for visual changes, public component parts for structural customization, and explicit props for instance behavior. The surrounding site lets you compare light and dark, different brands and density.
Source and verification
The examples below import actual public package entries. Full source is downloadable. Source coverage does not establish keyboard, hydration or browser compatibility; those require the application test suite.
Shell height and scroll ownership
AppShell is a static, server-safe layout. Its defaults remain height="viewport" and scroll="document". Use height="content" with mainAs="section" for embedded previews. Use height="parent" inside an explicitly bounded parent. scroll="workspace" makes the main area and sidebar scroll independently for viewport/parent modes; content mode remains intrinsic. Header/footer remain outside that main scroller.
Responsive navigation, collapse state, routing and domain records belong to the application. The workshop demonstrates a desktop rail, a narrow-screen Drawer with dismissal/focus return, and hash-based list/detail/settings history. Its compact default is an application preference; the provider default remains comfortable.
The shell example below creates and filters actual local drafts. The full workshop keeps saved data in versioned sessionStorage for the current tab, with an in-memory fallback and explicit reset. Drafts remain separate until save; cancel restores the saved baseline. Deletion offers undo until another deletion, dismissal or reload. The local service toggle demonstrates failure, cancellation and retry without contacting a backend.
Transfer-list bulk selection
TransferList.showSelectAll adds a select-all/none checkbox and selected-count feedback per side. It ignores disabled items, reports a mixed state for partial selection, and keeps the item value controlled. Moving items restores focus to the destination group, including an empty group. getSelectAllLabel and the existing labels/announcements support localization.
Compact composition defaults
PageHeader defaults to the lg title size; use titleSize to select another visual size independently of headingLevel. The surrounding Stack owns the gap to filters and content.
CommandPalette focuses its search input when opened. Its header includes an icon dismissal action with the accessible closeLabel; closeIcon replaces the artwork. Results scroll within the bounded palette and no-results text occupies space only when present.
Live examples
Try the controls below, then read or copy the complete example to adapt it for your application.
Application shell
Projects
Application patterns compose the same primitives.
No projects in this example
Create a draft to begin.
Form section and transfer pattern
Command palette and optional action docks
Read the complete example
'use client';
import { useState } from 'react';
import { Folder, Activity, Plus, X } from '@lucide/icons';
import { Icon } from '@fayegram-ai/faye-ui/core/icon';
import { AppShell } from '@fayegram-ai/faye-ui/patterns/shell';
import { PageHeader, EmptyState, FormSection, FilterBar } from '@fayegram-ai/faye-ui/patterns/content';
import { AppBar, BottomNavigation } from '@fayegram-ai/faye-ui/patterns/application-navigation';
import { SearchField } from '@fayegram-ai/faye-ui/patterns/search-field';
import { CommandPalette } from '@fayegram-ai/faye-ui/patterns/command-palette';
import { TransferList } from '@fayegram-ai/faye-ui/patterns/transfer-list';
import { FloatingAction, FloatingActions } from '@fayegram-ai/faye-ui/patterns/floating-actions';
import { Button } from '@fayegram-ai/faye-ui/controls/button';
import { TextField } from '@fayegram-ai/faye-ui/controls/text-field';
import { Stack } from '@fayegram-ai/faye-ui/layout/layout';
import { Section, Result } from '../example';
export default function Patterns() {
const [query, setQuery] = useState(''), [command, setCommand] = useState(false), [result, setResult] = useState('No command selected'), [chosen, setChosen] = useState<string[]>(['design']), [dock, setDock] = useState(false), [action, setAction] = useState(false);
const [projects, setProjects] = useState<string[]>([]);
const create = () => { setProjects(all => [...all, `Draft project ${all.length + 1}`]); setQuery(''); setResult('Draft created in the workspace above'); };
const matches = projects.filter(project => project.toLowerCase().includes(query.toLowerCase()));
return <><Section title="Application shell"><AppShell height="content" sidebar={<nav aria-label="Preview navigation"><a href="#workspace-preview">Workspace</a></nav>} header={<AppBar>Example workspace</AppBar>} mainAs="section" mainId="workspace-preview" mainLabel="Workspace example"><Stack gap="4"><PageHeader headingLevel={3} title="Projects" description="Application patterns compose the same primitives." actions={<Button onClick={() => setCommand(true)}>Commands</Button>}/><FilterBar><SearchField label="Filter example projects" placeholder="Find a project" value={query} onChange={e => setQuery(e.target.value)}/><Button onClick={() => setQuery('')}>Clear</Button></FilterBar>{matches.length ? <><ul aria-label="Example projects">{matches.map(project => <li key={project}>{project}</li>)}</ul><Button onClick={create}>Create draft</Button></> : <EmptyState title={projects.length ? 'No matching projects' : 'No projects in this example'} description={projects.length ? 'Clear the filter or create another draft.' : 'Create a draft to begin.'} action={<Button onClick={create}>Create draft</Button>}/>}</Stack></AppShell><BottomNavigation current="projects" items={[{ key: 'projects', label: 'Projects', icon: <Icon icon={Folder}/>, href: '#workspace-preview' }, { key: 'activity', label: 'Activity', icon: <Icon icon={Activity}/>, href: '/docs/display/' }]}/></Section><Section title="Form section and transfer pattern"><FormSection title="Project settings" description="Preferences remain application-owned."><TextField label="Team" defaultValue="Design systems"/></FormSection><TransferList showSelectAll items={[{ id: 'design', label: 'Design' }, { id: 'engineering', label: 'Engineering' }, { id: 'research', label: 'Research' }]} value={chosen} onValueChange={setChosen}/><Result>Selected: {chosen.join(', ')}</Result></Section><Section title="Command palette and optional action docks"><Button onClick={() => setCommand(true)}>Open commands</Button><CommandPalette open={command} onOpenChange={setCommand} commands={[{ id: 'create', label: 'Create project', keywords: ['new'] }, { id: 'archive', label: 'Archive latest project', disabled: projects.length === 0 }, { id: 'publish', label: 'Publish', disabled: true }]} onSelect={item => { if (item.id === 'create') create(); else if (item.id === 'archive') { setProjects(all => all.slice(0, -1)); setResult('Latest project archived'); } }}/><Button onClick={() => { setDock(v => !v); setAction(false); }}>Toggle action menu dock</Button><Button onClick={() => { setAction(v => !v); setDock(false); }}>Toggle single floating action</Button>{dock && <FloatingActions label="Quick actions" items={[{ id: 'create', label: <><Icon icon={Plus}/>Create a draft</>, onSelect: create }, { id: 'close', label: <><Icon icon={X}/>Hide dock</>, onSelect: () => setDock(false) }]}/>} {action && <FloatingAction onClick={() => { create(); setAction(false); }}><Icon icon={Plus}/>Create draft</FloatingAction>}<Result>{result}</Result></Section></>;
}