Compose recipes, not div soup
Build from Tabs, Table, Sheet and Card, not stacked bordered divs.
From shadcn (Vercel plugin): Composition recipes and Anti-patterns.
Same settings, two structures
Edit and save both, then outline the boxes.
Stacked bordered divsBad
Tabs + Card per groupGood
<Tabs defaultValue="profile">
<TabsList>
<TabsTrigger value="profile">Profile</TabsTrigger>
<TabsTrigger value="notifications">Notifications</TabsTrigger>
</TabsList>
<TabsContent value="profile">
<Card>
<CardHeader>
<CardTitle>Profile</CardTitle>
<CardDescription>How teammates see you.</CardDescription>
</CardHeader>
<CardContent className="space-y-4">{/* Form fields */}</CardContent>
<Separator />
<CardFooter className="justify-end">
<Button disabled={!form.formState.isDirty}>Save</Button>
</CardFooter>
</Card>
</TabsContent>
</Tabs>
Pick a use case, get a recipe
Type what you’re building, or pick a use case.
Div per regionBad
The recipeGood
Card in card in card
Drag to add grouping levels.
One card per levelBad
Levels in one headerGood
<Card>
<CardHeader>
<CardDescription>Workspace / Billing / Plan</CardDescription>
<CardTitle>Usage</CardTitle>
</CardHeader>
<Separator />
<CardContent>{/* seats */}</CardContent>
</Card>
Why it matters, pros and cons
Why it matters
A border says “separate”. Around everything, it says nothing.
Pros
- One Card and a Separator carry grouping.
- Overlays bring focus and dismiss for free.
- Content keeps its width at any depth.
Cons
- Dense data tools may want only a Table.
- A nested card is fine for a different object.
- Heavy primitives like Sheet and Command cost bundle size.