Figma v3 docsView v2 docs

Figma to React mapping

How shadcncraft components in Figma map to their React implementation. A small set of conventions covers every component, so you do not need to memorise them one by one.
PreviousNext

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 pageFigma componentReact exportImport path
CardCardCard@/components/ui/card
CardCardHeaderCardHeader@/components/ui/card
ButtonButtonButton@/components/ui/button
TooltipTooltipContentTooltipContent@/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 FigmaReact exportdata-slot
Card componentCardcard
CardHeader componentCardHeadercard-header
Title text layer in CardHeaderCardTitlecard-title
Description text layer in CardHeaderCardDescriptioncard-description
cardAction slot in CardHeaderCardActioncard-action
cardContent slot in CardCardContentcard-content
CardFooter componentCardFootercard-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 propertyReact propAllowed values
variantvariantdefault, destructive, outline, secondary, ghost, link
sizesizedefault, 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 FigmaReact exportdata-slot
DialogContent componentDialogContentdialog-content
DialogHeader componentDialogHeaderdialog-header
Title text layer in DialogHeaderDialogTitledialog-title
Description text layer in DialogHeaderDialogDescriptiondialog-description
DialogFooter componentDialogFooterdialog-footer
DialogClose componentDialogClosedialog-close
not in FigmaDialogTriggerdialog-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:

  1. The data-slot="..." attribute on each function block. 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.
  2. The cva(...) call, if present. The keys under variants: are the allowed Figma variant properties and their values.
  3. 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 DialogPortal and DialogOverlay that 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 same data-slot naming, you just rarely need them in a design file.
  • Charts (the Chart component 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.

SBIOTJ

Join 16,000+ builders shipping with shadcncraft

Design and assemble full pages faster with production-ready blocks built for real products.
  • Production-ready blocks and components
  • Clean React and Tailwind parity
  • Built for SaaS, marketing, and ecommerce teams
Get the design system
Join Now CTA

Real support from the team behind shadcncraft

Get help within 24 hours from the people who build and maintain the system.
Email
Prefer a direct line? Send us a message and we'll get back to you as soon as possible.
Discord
Get quick support, share feedback, or connect with other builders.
Feedback
Got something to say about anything shadcncraft? We'd love to hear it.