Figma to React mapping
The shadcncraft Figma kit and the React registry are designed to mirror each other. Once you understand the small set of conventions below, you can predict how any component in the kit maps to its React code, including new ones we add later.
This page is convention-first by design. We do not document every component one by one, because that would go out of date the moment we ship a new one. Instead, learn the rules, then read the React source for any component as the source of truth.
The four conventions
1. Component name
The Figma component name matches the React export name. Each component has its own page, and sub-components that exist as their own Figma component sit on that page, also named after their React export.
| Figma page | Figma component | React export | Import path |
|---|---|---|---|
| Card | Card | Card | @/components/ui/card |
| Card | CardHeader | CardHeader | @/components/ui/card |
| Button | Button | Button | @/components/ui/button |
| Tooltip | TooltipContent | TooltipContent | @/components/ui/tooltip |
If you see a component in Figma called DialogContent, the corresponding React component is DialogContent from @/components/ui/dialog.
Pro items follow the same idea with the registry slug in Title Case: Footer 4 on the Footers page installs as footer-4, and Chat Shell 1 on the Chat Blocks page installs as chat-shell-1.
2. Every component carries its data-slot
Every React component in shadcn/ui carries a data-slot attribute. In the kit, that value is written into the Figma component's description, so a component and its React counterpart share one anchor.
For the Figma component CardHeader, the description reads data-slot="card-header", and the matching React subcomponent is the one rendered with data-slot="card-header", exported as CardHeader.
The rule:
Figma component name = React export = PascalCase of the data-slot in its description
Inside a component, layers are named for what they hold rather than for a slot: the text layers in CardHeader are Title and Description, and the areas you fill are slot properties named after the React export in camelCase (cardAction, cardContent). AI tools reading the file through Figma MCP get the data-slot from the description and map straight back to the React part.
3. Variant properties are React props
Figma variant properties on a component (for example variant, size, state) map directly to React props of the same name, and the values are spelled the way React spells them (sm, lg, destructive). The allowed values match the variants block in the component's CVA definition.
To find the full list for any component, look at the cva(...) call near the top of the React file. The keys under variants: are the prop names, and the keys under each are the allowed values.
4. States are behavioural, not structural
State variants in Figma (hover, focus, active, disabled, loading, etc.) exist so designers can show what the component looks like in each state. They do not become React props. In code, the same look is produced automatically by Tailwind state variants (hover:, focus-visible:, data-[state=open]:, etc.) inside the component itself.
When designing, use Figma state variants to communicate intent. When implementing, do not look for a state prop, the component handles it.
Worked examples
Card — pure composition
Card is the clearest example of the slot convention because it has no variants beyond size, just parts.
In Figma the Card page holds three components: Card, CardHeader and CardFooter. Card places a CardHeader instance, a cardContent slot and a CardFooter instance; CardHeader holds the Title and Description text layers and a cardAction slot. In React you compose the same thing from the matching subcomponents.
import {
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
<Card>
<CardHeader>
<CardTitle>Plan</CardTitle>
<CardDescription>You're on the Pro plan.</CardDescription>
<CardAction>
<Button variant="outline" size="sm">
Change
</Button>
</CardAction>
</CardHeader>
<CardContent>Renews on July 12.</CardContent>
<CardFooter>Manage billing in settings.</CardFooter>
</Card>;| In Figma | React export | data-slot |
|---|---|---|
Card component | Card | card |
CardHeader component | CardHeader | card-header |
Title text layer in CardHeader | CardTitle | card-title |
Description text layer in CardHeader | CardDescription | card-description |
cardAction slot in CardHeader | CardAction | card-action |
cardContent slot in Card | CardContent | card-content |
CardFooter component | CardFooter | card-footer |
Button — variants and sizes
Button has no slots but does have two variant properties. Both map directly to React props.
import { Button } from "@/components/ui/button";
<Button variant="outline" size="lg">
Save
</Button>;The full list of allowed values comes straight from the CVA definition in button.tsx:
| Figma variant property | React prop | Allowed values |
|---|---|---|
| variant | variant | default, destructive, outline, secondary, ghost, link |
| size | size | default, xs, sm, lg, icon, icon-xs, icon-sm, icon-lg |
When we add a new variant or size, this list updates in button.tsx automatically and the Figma variant property gains the same value. No mapping doc to update.
Dialog — compound, with runtime-only parts
Dialog is a compound component. In React it has a root, a trigger, a portal, an overlay and the content. The kit ships only the parts that have a visual: DialogContent, with DialogHeader, DialogFooter and DialogClose as their own components on the same page. Root, trigger, portal and overlay are runtime-only primitives, so there is nothing to draw.
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
<Dialog>
<DialogTrigger asChild>
<Button>Open</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure?</DialogTitle>
<DialogDescription>This action cannot be undone.</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline">Cancel</Button>
<Button variant="destructive">Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>;There is no open or closed variant in Figma. The component opens because DialogTrigger was clicked, and the open-state styling is applied by data-[state=open]:... Tailwind variants inside DialogContent. The only stateful part is DialogClose, which carries state for its enabled, hover and focus looks.
| In Figma | React export | data-slot |
|---|---|---|
DialogContent component | DialogContent | dialog-content |
DialogHeader component | DialogHeader | dialog-header |
Title text layer in DialogHeader | DialogTitle | dialog-title |
Description text layer in DialogHeader | DialogDescription | dialog-description |
DialogFooter component | DialogFooter | dialog-footer |
DialogClose component | DialogClose | dialog-close |
| not in Figma | DialogTrigger | dialog-trigger |
Reading a component as source of truth
The React file for any component is the canonical reference. To answer "what does this Figma component become in React", open the matching file in components/ui/ and look for:
- The
data-slot="..."attribute on eachfunctionblock. This gives you the slot name and tells you which React subcomponent matches which Figma component, because the same value sits in that component's description. - The
cva(...)call, if present. The keys undervariants:are the allowed Figma variant properties and their values. - Anything wrapped in
data-[state=...]:...Tailwind classes. These are state-driven styles, handled automatically.
If you cannot find a Figma component or layer that matches a data-slot value, or a Figma variant property that matches a CVA variant, that is a kit gap worth filing.
When the convention does not fit
A small number of components legitimately diverge from the convention:
- Primitives wrapped from Radix (Dialog, Tabs, Popover, etc.) expose extra subcomponents like
DialogPortalandDialogOverlaythat are rarely composed in Figma. In Figma, designers usually only show the trigger and content. The other subcomponents still exist in React and follow the samedata-slotnaming, you just rarely need them in a design file. - Charts (the
Chartcomponent family) take data, not layers, so the Figma version is a static rendering and the React version is data-driven. There is no slot-for-slot mapping.
If you hit something that does not fit, treat it as documentation owed on that specific component rather than a problem with the convention.
Related
- Figma components for editing components in Figma
- React export for generating React code from a Figma selection
- Variables for the token system that underlies both sides
- From Figma to production React, with AI in the loop for the end-to-end workflow that puts these conventions to work
