Skip to main content

Card

Group related content. Build structured, scannable layouts.

Production deploy

Live

web-app, commit 8f2c1ad on main

Build time
42s
Triggered by
lucia@acmestudio.dev
Finished
12 minutes ago
1import { Badge } from '@/components/ui/badge';
2import { Button } from '@/components/ui/button';
3import { Card } from '@/components/ui/card';
4
5export function Default() {
6 return (
7 <Card className="w-full max-w-sm">
8 <Card.Header>
9 <div className="flex items-center justify-between gap-3">
10 <Card.Title>Production deploy</Card.Title>
11 <Badge variant="success">Live</Badge>
12 </div>
13 <Card.Description>web-app, commit 8f2c1ad on main</Card.Description>
14 </Card.Header>
15 <Card.Content className="text-sm">
16 <dl className="grid grid-cols-2 gap-y-2">
17 <dt className="text-muted-foreground">Build time</dt>
18 <dd className="text-right">42s</dd>
19 <dt className="text-muted-foreground">Triggered by</dt>
20 <dd className="text-right">lucia@acmestudio.dev</dd>
21 <dt className="text-muted-foreground">Finished</dt>
22 <dd className="text-right">12 minutes ago</dd>
23 </dl>
24 </Card.Content>
25 <Card.Footer align="between">
26 <Button variant="ghost" size="sm">
27 Roll back
28 </Button>
29 <Button variant="secondary" size="sm">
30 View logs
31 </Button>
32 </Card.Footer>
33 </Card>
34 );
35}

Installation

pnpm dlx nachui add card

Anatomy

1import { Card } from '@/components/ui/card';
1<Card>
2 <Card.Header>
3 <Card.Title>Card Title</Card.Title>
4 <Card.Description>Card Description</Card.Description>
5 </Card.Header>
6 <Card.Content>
7 <p>Card Content</p>
8 </Card.Content>
9 <Card.Footer>
10 <p>Card Footer</p>
11 </Card.Footer>
12</Card>

Composition

Use the following composition to build a Card:
Card
├── Card.Header
│ ├── Card.Title
│ └── Card.Description
├── Card.Content
└── Card.Footer

Variants

Outline

Cards with a prominent border and no shadow.

ci-deploy-key

Read and write

Created Jan 9, last used 3 hours ago

sk_live_4f8a...c210
1import { Badge } from '@/components/ui/badge';
2import { Button } from '@/components/ui/button';
3import { Card } from '@/components/ui/card';
4
5export function Outline() {
6 return (
7 <Card variant="outline" className="w-full max-w-sm">
8 <Card.Header>
9 <div className="flex items-center justify-between gap-3">
10 <Card.Title>ci-deploy-key</Card.Title>
11 <Badge variant="outline">Read and write</Badge>
12 </div>
13 <Card.Description>Created Jan 9, last used 3 hours ago</Card.Description>
14 </Card.Header>
15 <Card.Content className="text-sm">
16 <code className="bg-muted text-muted-foreground block truncate rounded px-2 py-1.5 font-mono text-xs">
17 sk_live_4f8a...c210
18 </code>
19 </Card.Content>
20 <Card.Footer align="between">
21 <Button variant="ghost" size="sm">
22 Copy
23 </Button>
24 <Button variant="ghost" size="sm" className="text-destructive-text">
25 Revoke
26 </Button>
27 </Card.Footer>
28 </Card>
29 );
30}

Ghost

Transparent cards without border or shadow.

Notifications

How this workspace reaches you.

  • Deploy failuresEmail and Slack
  • New commentsSlack only
  • Weekly usage reportEmail, every Monday
1import { Card } from '@/components/ui/card';
2
3const channels = [
4 { name: 'Deploy failures', detail: 'Email and Slack' },
5 { name: 'New comments', detail: 'Slack only' },
6 { name: 'Weekly usage report', detail: 'Email, every Monday' },
7];
8
9export function Ghost() {
10 return (
11 <div className="border-border bg-card w-full max-w-sm rounded-xl border p-2">
12 <Card variant="ghost">
13 <Card.Header>
14 <Card.Title>Notifications</Card.Title>
15 <Card.Description>How this workspace reaches you.</Card.Description>
16 </Card.Header>
17 <Card.Content className="text-sm">
18 <ul className="space-y-2">
19 {channels.map((channel) => (
20 <li key={channel.name} className="flex items-baseline justify-between gap-3">
21 <span>{channel.name}</span>
22 <span className="text-muted-foreground text-xs">{channel.detail}</span>
23 </li>
24 ))}
25 </ul>
26 </Card.Content>
27 </Card>
28 </div>
29 );
30}

Compact

Cards with reduced padding for dense layouts.

Default density, billing page

Team plan

$49 per month, renews on Mar 14

12 of 20 seats used. Invoices go to billing@acmestudio.dev.

Compact density, sidebar summary

Team plan

$49 per month, renews on Mar 14

12 of 20 seats used. Invoices go to billing@acmestudio.dev.

1import { Button } from '@/components/ui/button';
2import { Card } from '@/components/ui/card';
3
4const densities = [
5 {
6 label: 'Default density, billing page',
7 compact: false,
8 },
9 {
10 label: 'Compact density, sidebar summary',
11 compact: true,
12 },
13] as const;
14
15export function Compact() {
16 return (
17 <div className="grid w-full max-w-sm gap-6">
18 {densities.map((density) => (
19 <div key={density.label}>
20 <p className="text-muted-foreground mb-2 text-xs">{density.label}</p>
21 <Card>
22 <Card.Header compact={density.compact}>
23 <Card.Title>Team plan</Card.Title>
24 <Card.Description>$49 per month, renews on Mar 14</Card.Description>
25 </Card.Header>
26 <Card.Content compact={density.compact} className="text-sm">
27 <p className="text-muted-foreground">
28 12 of 20 seats used. Invoices go to billing@acmestudio.dev.
29 </p>
30 </Card.Content>
31 <Card.Footer compact={density.compact}>
32 <Button variant="secondary" size="sm">
33 Manage billing
34 </Button>
35 </Card.Footer>
36 </Card>
37 </div>
38 ))}
39 </div>
40 );
41}

API Reference

Card

PropTypeDefaultDescription
variant"default" | "outline" | "ghost""default"Visual variant of the card.
asReact.ElementType'div'Changes the root element of the card.
classNamestringAdditional classes.
childrenReact.ReactNodeCard content.

Card.Header

PropTypeDefaultDescription
compactbooleanfalseReduces top and bottom padding of the header.
classNamestringAdditional classes.
childrenReact.ReactNodeHeader content.

Card.Title

PropTypeDefaultDescription
as'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6''h3'HTML tag for the card title.
classNamestringAdditional classes.
childrenReact.ReactNodeTitle content.

Card.Description

PropTypeDefaultDescription
classNamestringAdditional classes.
childrenReact.ReactNodeDescription text.

Card.Content

PropTypeDefaultDescription
compactbooleanfalseReduces content padding.
classNamestringAdditional classes.
childrenReact.ReactNodeMain content of the card.

Card.Footer

PropTypeDefaultDescription
align'start' | 'center' | 'end' | 'between''start'Controls horizontal alignment of the footer.
compactbooleanfalseReduces footer padding.
classNamestringAdditional classes.
childrenReact.ReactNodeFooter content.
Found something to improve?

Notice a bug, typo, or missing detail on this page? Help us make the documentation better by opening a GitHub issue.

Create an Issue
Ctrl+I