DittoSheet
DittoSheet provides a consistent slide-out panel experience with standard header, content, and footer patterns. Use it when you need predictable sheet behavior with built-in close controls.
When to Use
Use When
- Displaying entity details or forms in a slide-out panel
- Need consistent header/footer patterns
- Want standard close behavior control
Skip When
- Need completely custom sheet layout (use Sheet primitive)
- Content is simple enough for a Dialog
Basic Usage
import { DittoSheet, FormFooter } from '@/ui/components';
import { FormBlock, FormTextField } from '@/ui/blocks';
const schema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
// FormFooter pattern - footer in DittoSheet, form inside content
<DittoSheet
open={open}
onOpenChange={setOpen}
title={t('user.editTitle')}
description={t('user.editDescription')}
footer={
<FormFooter
formId="user-form"
cancelLabel={t('common.actions.cancel')}
onCancel={() => setOpen(false)}
submitLabel={t('common.actions.save')}
isSubmitting={isSubmitting}
/>
}
>
<FormBlock
id="user-form"
schema={schema}
onSubmit={handleSubmit}
hideActions
>
<FormTextField name="name" label={t('form.name')} />
<FormTextField name="email" label={t('form.email')} />
</FormBlock>
</DittoSheet>Size Variants
Two size options to match content complexity
| Size | Width | Use Case |
|---|---|---|
| normal | 448px | Quick view, minimal fields |
| wide | 512px | Rich forms, multiple sections |
// Normal size (448px) - Quick view, minimal fields <DittoSheet size="normal" ... /> // Wide size (512px) - Rich forms, multiple sections <DittoSheet size="wide" ... />
Footer Actions
Use DittoSheetFooter for consistent action button layout
// FormFooter with formId pattern (RECOMMENDED)
<DittoSheet
open={open}
onOpenChange={setOpen}
title={t('settings.title')}
footer={
<FormFooter
formId="settings-form"
cancelLabel={t('common.actions.cancel')}
onCancel={() => setOpen(false)}
submitLabel={t('common.actions.save')}
isSubmitting={isSubmitting}
/>
}
>
<FormBlock
id="settings-form"
schema={schema}
onSubmit={handleSubmit}
hideActions
>
<FormTextField name="name" label={t('settings.name')} />
</FormBlock>
</DittoSheet>Close Behavior
Control whether clicking outside closes the sheet
Set closeOnOutsideClick to false to require explicit close action.
Props Reference
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | required | Whether the sheet is open |
onOpenChange | (open: boolean) => void | required | Callback when open state changes |
title | string | required | Sheet title (required for accessibility) |
description | string | - | Optional description below title |
children | ReactNode | required | Sheet content |
footer | ReactNode | - | Optional footer content (use DittoSheetFooter) |
size | 'normal' | 'wide' | 'normal' | Sheet width: normal (448px) or wide (512px) |
side | 'left' | 'right' | 'top' | 'bottom' | 'right' | Which side the sheet slides in from |
closeOnOutsideClick | boolean | true | Whether clicking outside closes the sheet |
className | string | - | Additional CSS classes for SheetContent |