Resend Design System
Use primitives from src/ui/ for all UI work. Never create custom components when a primitive exists. Check /design/components pages for live examples.
Quick Reference
All primitives use @/ui/{name} imports. Icons in @/ui/icons/icon-{name}.
Core Components
| Component |
Import |
Key Variants |
| Button |
@/ui/button |
appearance: white, gray, fade-gray, fade, fade-red, red. size: 1, 2 |
| TextField |
@/ui/text-field/text-field |
Compound: Root > Slot + Input + Slot. state/size on Input. size: 1, 2, 3 |
| Heading |
@/ui/heading |
size: 1-8. color: white, gray. weight: medium, semibold, bold |
| Text |
@/ui/text |
size: 1-9. color: white, gray, red, yellow |
| Tag |
@/ui/tag |
appearance: gray, green, red, yellow, blue, orange, violet, sand |
| Banner |
@/ui/banner |
Page/section-level messages (role="alert"). Auto icon. green=success, yellow=warning, red=error, blue=info. Use Tag for inline item labels |
| Select |
@/ui/select |
Namespace: Root > Trigger + Content > Item. For value selection (forms) |
| Dialog |
@/ui/dialog |
Namespace: Root > Trigger + Content > Title. size: 1, 2, full-screen |
| Switch |
@/ui/switch |
checked, onCheckedChange, disabled |
| Checkbox |
@/ui/checkbox |
checked (boolean | 'indeterminate'), onCheckedChange |
| IconButton |
@/ui/icon-button |
Same variants as Button. Always provide aria-label |
| DropdownMenu |
@/ui/dropdown-menu |
Namespace: Root > Trigger + Content > Item. For actions, not value selection |
Sizing Scale
| Size |
Height |
Text |
Radius |
'1' |
h-6 |
text-xs |
rounded-lg |
'2' |
h-8 |
text-sm |
rounded-xl |
'3' |
h-10 |
text-sm |
rounded-xl |
Color Conventions
- Primary action:
appearance="white" (inverted black/white, flips in dark mode)
- Secondary:
appearance="gray"
- Subtle/ghost:
appearance="fade" or appearance="fade-gray"
- Destructive:
appearance="red" or appearance="fade-red"
Key Rules
- Use
cn() from @/lib/cn for class merging
- Use
@/ absolute imports everywhere
- Prefer Server Components;
'use client' only at lowest interactive leaf — extract the interactive part into a small leaf component, don't mark the whole page client
- Use sentence case for all UI copy
- State via
state prop, not booleans — state="loading", state="disabled", state="invalid", state="read-only". Each value is self-sufficient: state="loading" already prevents interaction, so don't also add disabled={}
- Compound TextField:
TextField.Root > TextField.Slot? + TextField.Input + TextField.Slot? — state and size go on TextField.Input, not Root. Use <TextField.Error message={msg} id="x" /> inside a trailing Slot for validation errors — it auto-wires aria-describedby. Don't also pass error= on Input or set aria-describedby manually.
- Radix namespaces:
import * as Select from '@/ui/select'
asChild for links: <Button asChild><Link href="/x">Label</Link></Button> — also works on Dialog.Trigger, Tooltip.Trigger
Server vs Client
Already client (built-in, no extra 'use client' needed on your part): TextField, Checkbox, Dialog, Drawer, Collapsible, Calendar, BulkActions.
Server-safe: Button, Heading, Text, Tag, Banner, Card, EmptyState, Kbd, IconButton.
Correct pattern — extract only the interactive leaf:
// page.tsx — Server Component
export default function Page() {
return <Card><Heading>Title</Heading><DeleteDialog /></Card>;
}
// delete-dialog.tsx — small Client Component
'use client';
export function DeleteDialog() {
return (
<Dialog.Root>
<Dialog.Trigger asChild><Button appearance="fade-red">Delete</Button></Dialog.Trigger>
<Dialog.Content size="1"><Dialog.Title>Confirm delete</Dialog.Title></Dialog.Content>
</Dialog.Root>
);
}
References
For detailed documentation, load these as needed:
design-system/references/components.md — Full component catalog with all props and usage
design-system/references/design-tokens.md — Colors, typography, shadows, animations
design-system/references/patterns.md — CVA conventions, compound components, slot system
1---2name: resend-design-system3description: Use when building or modifying UI in the Resend codebase. Provides component APIs, variant options, design tokens, and composition patterns for all src/ui/ primitives.4---56# Resend Design System78Use primitives from `src/ui/` for all UI work. Never create custom components when a primitive exists. Check `/design/components` pages for live examples.910## Quick Reference1112All primitives use `@/ui/{name}` imports. Icons in `@/ui/icons/icon-{name}`.1314### Core Components1516| Component | Import | Key Variants |17|-----------|--------|-------------|18| Button | `@/ui/button` | appearance: white, gray, fade-gray, fade, fade-red, red. size: 1, 2 |19| TextField | `@/ui/text-field/text-field` | Compound: Root > Slot + Input + Slot. `state`/`size` on **Input**. size: 1, 2, 3 |20| Heading | `@/ui/heading` | size: 1-8. color: white, gray. weight: medium, semibold, bold |21| Text | `@/ui/text` | size: 1-9. color: white, gray, red, yellow |22| Tag | `@/ui/tag` | appearance: gray, green, red, yellow, blue, orange, violet, sand |23| Banner | `@/ui/banner` | Page/section-level messages (`role="alert"`). Auto icon. green=success, yellow=warning, red=error, blue=info. Use Tag for inline item labels |24| Select | `@/ui/select` | Namespace: Root > Trigger + Content > Item. For **value selection** (forms) |25| Dialog | `@/ui/dialog` | Namespace: Root > Trigger + Content > Title. size: 1, 2, full-screen |26| Switch | `@/ui/switch` | checked, onCheckedChange, disabled |27| Checkbox | `@/ui/checkbox` | checked (boolean \| 'indeterminate'), onCheckedChange |28| IconButton | `@/ui/icon-button` | Same variants as Button. **Always** provide `aria-label` |29| DropdownMenu | `@/ui/dropdown-menu` | Namespace: Root > Trigger + Content > Item. For **actions**, not value selection |3031### Sizing Scale3233| Size | Height | Text | Radius |34|------|--------|------|--------|35| `'1'` | h-6 | text-xs | rounded-lg |36| `'2'` | h-8 | text-sm | rounded-xl |37| `'3'` | h-10 | text-sm | rounded-xl |3839### Color Conventions4041- Primary action: `appearance="white"` (inverted black/white, flips in dark mode)42- Secondary: `appearance="gray"`43- Subtle/ghost: `appearance="fade"` or `appearance="fade-gray"`44- Destructive: `appearance="red"` or `appearance="fade-red"`4546### Key Rules47481. Use `cn()` from `@/lib/cn` for class merging492. Use `@/` absolute imports everywhere503. Prefer Server Components; `'use client'` only at lowest interactive leaf — **extract** the interactive part into a small leaf component, don't mark the whole page client514. Use sentence case for all UI copy525. State via `state` prop, not booleans — `state="loading"`, `state="disabled"`, `state="invalid"`, `state="read-only"`. Each value is self-sufficient: `state="loading"` already prevents interaction, so don't also add `disabled={}`536. Compound TextField: `TextField.Root > TextField.Slot? + TextField.Input + TextField.Slot?` — `state` and `size` go on **`TextField.Input`**, not Root. Use `<TextField.Error message={msg} id="x" />` inside a trailing Slot for validation errors — it auto-wires `aria-describedby`. Don't also pass `error=` on Input or set `aria-describedby` manually.547. Radix namespaces: `import * as Select from '@/ui/select'`558. `asChild` for links: `<Button asChild><Link href="/x">Label</Link></Button>` — also works on Dialog.Trigger, Tooltip.Trigger5657### Server vs Client5859Already client (built-in, no extra `'use client'` needed on your part): TextField, Checkbox, Dialog, Drawer, Collapsible, Calendar, BulkActions.6061Server-safe: Button, Heading, Text, Tag, Banner, Card, EmptyState, Kbd, IconButton.6263**Correct pattern** — extract only the interactive leaf:64```tsx65// page.tsx — Server Component66export default function Page() {67 return <Card><Heading>Title</Heading><DeleteDialog /></Card>;68}6970// delete-dialog.tsx — small Client Component71'use client';72export function DeleteDialog() {73 return (74 <Dialog.Root>75 <Dialog.Trigger asChild><Button appearance="fade-red">Delete</Button></Dialog.Trigger>76 <Dialog.Content size="1"><Dialog.Title>Confirm delete</Dialog.Title></Dialog.Content>77 </Dialog.Root>78 );79}80```8182## References8384For detailed documentation, load these as needed:85- `design-system/references/components.md` — Full component catalog with all props and usage86- `design-system/references/design-tokens.md` — Colors, typography, shadows, animations87- `design-system/references/patterns.md` — CVA conventions, compound components, slot system