Guide
Real-world patterns that combine multiple Docyrus UI components. Copy, adapt, and use in your projects.
CRUD Table with Filters
A common pattern: a data grid with toolbar filters, search, and create/delete dialogs.
import { useState } from 'react';
import { DataGrid } from '@/components/docyrus/data-grid';
import { DataTableFilter } from '@/components/docyrus/data-table-filter';
import { SearchInput } from '@/components/docyrus/search-input';
import { DeleteConfirmDialog } from '@/components/docyrus/delete-confirm-dialog';
import { CreateRecordDialog } from '@/components/docyrus/create-record-dialog';
import { Button } from '@/components/ui/button';
export function ContactsPage() {
const [search, setSearch] = useState('');
const [filters, setFilters] = useState([]);
const [deleteId, setDeleteId] = useState<string | null>(null);
const [showCreate, setShowCreate] = useState(false);
return (
<div className="flex flex-col gap-4">
{/* Toolbar */}
<div className="flex items-center gap-3">
<SearchInput
value={search}
onChange={setSearch}
placeholder="Search contacts..."
className="w-64"
/>
<DataTableFilter
filters={filters}
onChange={setFilters}
fields={contactFields}
/>
<Button className="ml-auto" onClick={() => setShowCreate(true)}>
Add Contact
</Button>
</div>
{/* Grid */}
<DataGrid
data={filteredContacts}
columns={columns}
onRowAction={(action, row) => {
if (action === 'delete') setDeleteId(row.id);
}}
/>
{/* Dialogs */}
<CreateRecordDialog
open={showCreate}
onOpenChange={setShowCreate}
fields={contactFields}
onSubmit={handleCreate}
/>
<DeleteConfirmDialog
open={!!deleteId}
onOpenChange={() => setDeleteId(null)}
onConfirm={() => handleDelete(deleteId)}
title="Delete contact"
description="This action cannot be undone."
/>
</div>
);
}Form with Validation
Schema-driven form using TanStack Form with Docyrus form fields:
import { useForm } from '@tanstack/react-form';
import { DocyField } from '@/components/docyrus/form-fields/docy-field';
import { DocyFieldDate } from '@/components/docyrus/form-fields/docy-field-date';
import { DocyFieldSelect } from '@/components/docyrus/form-fields/docy-field-select';
import { Button } from '@/components/ui/button';
const priorityOptions = [
{ label: 'Low', value: 'low' },
{ label: 'Medium', value: 'medium' },
{ label: 'High', value: 'high' },
];
export function CreateTaskForm({ onSubmit }: { onSubmit: (data: TaskData) => void }) {
const form = useForm({
defaultValues: {
title: '',
description: '',
priority: 'medium',
dueDate: null as Date | null,
},
onSubmit: ({ value }) => onSubmit(value),
});
return (
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
className="flex flex-col gap-4"
>
<form.Field
name="title"
validators={{ onChange: ({ value }) => !value ? 'Title is required' : undefined }}
>
{(field) => (
<DocyField
label="Title"
value={field.state.value}
onChange={field.handleChange}
error={field.state.meta.errors[0]}
required
/>
)}
</form.Field>
<form.Field name="description">
{(field) => (
<DocyField
label="Description"
value={field.state.value}
onChange={field.handleChange}
multiline
rows={3}
/>
)}
</form.Field>
<div className="grid grid-cols-2 gap-4">
<form.Field name="priority">
{(field) => (
<DocyFieldSelect
label="Priority"
value={field.state.value}
onChange={field.handleChange}
options={priorityOptions}
/>
)}
</form.Field>
<form.Field name="dueDate">
{(field) => (
<DocyFieldDate
label="Due Date"
value={field.state.value}
onChange={field.handleChange}
/>
)}
</form.Field>
</div>
<Button type="submit" className="self-end">
Create Task
</Button>
</form>
);
}Dashboard Layout
Combine stats, charts, and activity panels into a dashboard view:
import { AwesomeStats } from '@/components/docyrus/awesome-stats';
import { RecordActivityPanel } from '@/components/docyrus/record-activity-panel';
import { NotificationsPanel } from '@/components/docyrus/notifications-panel';
import { Calendar } from '@/components/docyrus/calendar';
export function DashboardPage() {
return (
<div className="flex flex-col gap-6">
{/* Stats row */}
<AwesomeStats
stats={[
{ label: 'Total Revenue', value: '$48,200', change: 12.5 },
{ label: 'Active Users', value: '2,340', change: -3.2 },
{ label: 'Conversion', value: '3.24%', change: 0.8 },
{ label: 'Avg. Order', value: '$128', change: 5.1 },
]}
/>
{/* Main content + sidebar */}
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
<div className="lg:col-span-2">
<Calendar
view="week"
events={events}
onEventClick={handleEventClick}
/>
</div>
<div className="flex flex-col gap-4">
<NotificationsPanel limit={5} />
<RecordActivityPanel
recordId={currentRecordId}
limit={10}
/>
</div>
</div>
</div>
);
}File Attachment with Preview
Handle file uploads with preview and attachment management:
import { useState } from 'react';
import { FileAttachmentPanel } from '@/components/docyrus/file-attachment-panel';
import { ImageEditor } from '@/components/docyrus/image-editor';
import { AwesomeDialog } from '@/components/docyrus/awesome-dialog';
export function DocumentManager() {
const [files, setFiles] = useState<Attachment[]>([]);
const [editingImage, setEditingImage] = useState<string | null>(null);
return (
<>
<FileAttachmentPanel
attachments={files}
onUpload={async (newFiles) => {
const uploaded = await uploadFiles(newFiles);
setFiles((prev) => [...prev, ...uploaded]);
}}
onDelete={(id) => {
setFiles((prev) => prev.filter((f) => f.id !== id));
}}
onPreview={(file) => {
if (file.type.startsWith('image/')) {
setEditingImage(file.url);
}
}}
accept="image/*,.pdf,.docx"
maxSize={10 * 1024 * 1024}
/>
<AwesomeDialog
open={!!editingImage}
onOpenChange={() => setEditingImage(null)}
title="Edit Image"
size="xl"
>
{editingImage && (
<ImageEditor
src={editingImage}
onSave={async (editedBlob) => {
await saveEditedImage(editedBlob);
setEditingImage(null);
}}
/>
)}
</AwesomeDialog>
</>
);
}Kanban with Detail Panel
Project board with click-to-expand detail view:
import { useState } from 'react';
import { Kanban } from '@/components/docyrus/kanban';
import { AwesomeCard } from '@/components/docyrus/awesome-card';
import { CommentsPanel } from '@/components/docyrus/comments-panel';
import { EditableValue } from '@/components/docyrus/editable-value';
export function ProjectBoard() {
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
return (
<div className="flex h-[calc(100vh-4rem)] gap-4">
{/* Board */}
<div className="flex-1 overflow-hidden">
<Kanban
columns={boardColumns}
onCardClick={setSelectedTask}
onCardMove={handleMove}
/>
</div>
{/* Detail sidebar */}
{selectedTask && (
<AwesomeCard className="w-96 shrink-0 overflow-y-auto">
<div className="flex flex-col gap-4 p-4">
<EditableValue
value={selectedTask.title}
onSave={(val) => updateTask(selectedTask.id, { title: val })}
variant="heading"
/>
<EditableValue
value={selectedTask.description}
onSave={(val) => updateTask(selectedTask.id, { description: val })}
multiline
/>
<CommentsPanel
recordId={selectedTask.id}
comments={selectedTask.comments}
onAddComment={handleAddComment}
/>
</div>
</AwesomeCard>
)}
</div>
);
}Record Detail Page
Editable record with activity timeline, sharing, and delete confirmation:
import { EditableRecordDetail } from '@/components/docyrus/editable-record-detail';
import { RecordActivityPanel } from '@/components/docyrus/record-activity-panel';
import { RecordSharing } from '@/components/docyrus/record-sharing';
import { RecordDeleteConfirmDialog } from '@/components/docyrus/record-delete-confirm-dialog';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
export function RecordPage({ recordId }: { recordId: string }) {
return (
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
<div className="lg:col-span-2">
<EditableRecordDetail
recordId={recordId}
fields={recordFields}
onSave={handleSave}
/>
</div>
<div>
<Tabs defaultValue="activity">
<TabsList className="w-full">
<TabsTrigger value="activity">Activity</TabsTrigger>
<TabsTrigger value="sharing">Sharing</TabsTrigger>
</TabsList>
<TabsContent value="activity">
<RecordActivityPanel recordId={recordId} />
</TabsContent>
<TabsContent value="sharing">
<RecordSharing
recordId={recordId}
onShare={handleShare}
/>
</TabsContent>
</Tabs>
</div>
</div>
);
}Related
- Installation — Set up Docyrus UI in your project
- Components — Browse all web components
- API Reference — REST client documentation