TLDR
Component library handoff is the process of translating your Figma component library into a code component library (React, Vue, or Web Components) with 1:1 mapping, consistent naming conventions, and complete state documentation. Done right, it ensures designers and developers work from the same source of truth, eliminating back-and-forth about "how this should look."
Key takeaways:
- Every Figma component should have exactly one corresponding code component (1:1 mapping)
- Use prop-based variants (Button variant="primary") instead of creating separate components (PrimaryButton, SecondaryButton)
- Document all component states (default, hover, focus, disabled, loading, error) to prevent implementation gaps
- Automate handoff with tools like Figma plugins, Storybook, and design token pipelines
- Component library handoff is the foundation of scalable design system handoff
What is Component Library Handoff?
Component library handoff is the specific process of taking a Figma component library—your buttons, inputs, cards, modals, navigation bars—and translating them into reusable code components. Unlike general design handoff, which might involve one-off page implementations, component library handoff creates a reusable system that both designers and developers reference for all future work.
The goal is simple: when a designer uses a Button component in Figma, a developer should be able to use the exact same Button component in code with identical visual output and behavior. If they diverge, you end up with "design drift" where the mockups and production build no longer match.
Component library handoff is a subset of design system handoff. While a full design system includes tokens, documentation, patterns, and governance, the component library is the concrete implementation layer—the actual UI building blocks.
This process matters most for teams with multiple designers and developers working across different features. Without it, every developer interprets "primary button" differently, leading to inconsistent UIs and higher maintenance costs.
1:1 Mapping Between Design and Code
The golden rule of component library handoff: one Figma component equals one code component.
This sounds obvious, but it's commonly violated in practice. Here's what NOT to do:
Anti-pattern:
// Bad: Separate components for each variant
<PrimaryButton />
<SecondaryButton />
<DangerButton />
<SmallPrimaryButton />
<LargePrimaryButton />This approach leads to component explosion. If you have 3 variants and 3 sizes, you're maintaining 9 components instead of 1. Every change requires updating multiple files.
Correct pattern:
// Good: Single component with props
<Button variant="primary" size="medium" />
<Button variant="secondary" size="small" />
<Button variant="danger" size="large" />Now you have one component to maintain. Props control the visual variations, just like Figma variants.
Mapping Figma Variants to Props
Here's how Figma variant properties map to React props:
| Figma Concept | React Equivalent | Example |
|---|---|---|
| Component | React component | Button.tsx |
| Variant property | Prop with union type | variant: 'primary' | 'secondary' |
| Variant option | Prop value | variant="primary" |
| Boolean property | Boolean prop | disabled={true} |
| Instance | JSX element | <Button /> |
If your Figma Button has a "Variant" property with options Primary, Secondary, and Danger, your React component should have:
type ButtonVariant = 'primary' | 'secondary' | 'danger';
interface ButtonProps {
variant: ButtonVariant;
// ... other props
}Naming Conventions
Consistent naming prevents confusion and makes the handoff predictable. Here are the conventions we recommend:
Component Names
Use PascalCase and match Figma exactly:
- Figma:
Button→ Code:Button.tsx - Figma:
TextInput→ Code:TextInput.tsx - Figma:
NavigationBar→ Code:NavigationBar.tsx
Don't shorten or abbreviate unless Figma does:
- Figma:
Button→ Code:Button.tsx(notBtn.tsx) - Figma:
TextField→ Code:TextField.tsx(notInput.tsx)
The only exception: if your team already has established code conventions (e.g., all components prefixed with Ui), apply those consistently. The key is predictability.
Prop Names
Match Figma variant property names when possible:
- Figma property: "Variant" → Prop:
variant - Figma property: "Size" → Prop:
size - Figma property: "Has Icon" → Prop:
hasIcon(camelCase in code)
For values, use lowercase with hyphens in Figma, camelCase in code:
- Figma:
primary-action→ Code:'primaryAction' - Figma:
large→ Code:'large'(same)
Special Cases
Interactive states are boolean props:
disabled={true}loading={true}error={true}
Content is passed as children or dedicated props:
<Button>Click me</Button>(children)<TextField label="Email" placeholder="you@example.com" />(props)
Code Example: Props vs Variants
Here's a complete React Button component that mirrors a Figma component library structure.
Figma component setup:
- Component name: Button
- Variant property: Variant (Primary, Secondary, Danger)
- Variant property: Size (Small, Medium, Large)
- Boolean property: Disabled
- Boolean property: Loading
React implementation:
// Button.tsx
import { ButtonHTMLAttributes, ReactNode } from 'react';
type ButtonVariant = 'primary' | 'secondary' | 'danger';
type ButtonSize = 'small' | 'medium' | 'large';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
loading?: boolean;
disabled?: boolean;
children: ReactNode;
}
export function Button({
variant = 'primary',
size = 'medium',
loading = false,
disabled = false,
children,
className = '',
...props
}: ButtonProps) {
const baseStyles = 'inline-flex items-center justify-center font-medium transition-colors rounded-md';
const variantStyles = {
primary: 'bg-blue-600 hover:bg-blue-700 text-white',
secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-900',
danger: 'bg-red-600 hover:bg-red-700 text-white',
};
const sizeStyles = {
small: 'px-3 py-1.5 text-sm',
medium: 'px-4 py-2 text-base',
large: 'px-6 py-3 text-lg',
};
const disabledStyles = 'opacity-50 cursor-not-allowed';
const classes = [
baseStyles,
variantStyles[variant],
sizeStyles[size],
(disabled || loading) && disabledStyles,
className,
].filter(Boolean).join(' ');
return (
<button
className={classes}
disabled={disabled || loading}
{...props}
>
{loading ? (
<>
<span className="mr-2">
<Spinner size={size} />
</span>
{children}
</>
) : children}
</button>
);
}Usage examples:
// Matches Figma: Button [Variant=Primary, Size=Medium]
<Button variant="primary" size="medium">
Save Changes
</Button>
// Matches Figma: Button [Variant=Danger, Size=Large, Disabled=True]
<Button variant="danger" size="large" disabled>
Delete Account
</Button>
// Matches Figma: Button [Variant=Secondary, Size=Small, Loading=True]
<Button variant="secondary" size="small" loading>
Loading...
</Button>This structure makes the relationship between Figma and code explicit. A designer looking at this code can immediately map it back to the Figma component.
Documenting Component States
Interactive components have multiple states that must be documented and implemented consistently. Missing states are a common source of bugs and design-code misalignment.
Essential states to document:
- Default – Component's initial appearance
- Hover – Mouse over (desktop only)
- Focus – Keyboard navigation or click focus
- Active – Currently being clicked/pressed
- Disabled – Interaction not allowed
- Loading – Async operation in progress
- Error – Validation failed or error occurred
Storybook for State Documentation
The best way to document component states is with Storybook. It creates a living component library where developers can see and interact with every state.
Here's a Storybook story showing all Button states:
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof Button>;
// Default state
export const Primary: Story = {
args: {
variant: 'primary',
children: 'Primary Button',
},
};
// All variants
export const AllVariants: Story = {
render: () => (
<div className="flex gap-4">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
</div>
),
};
// All sizes
export const AllSizes: Story = {
render: () => (
<div className="flex items-center gap-4">
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
<Button size="large">Large</Button>
</div>
),
};
// Interactive states
export const States: Story = {
render: () => (
<div className="flex flex-col gap-4">
<Button>Default</Button>
<Button disabled>Disabled</Button>
<Button loading>Loading</Button>
</div>
),
};
// State combinations
export const AllCombinations: Story = {
render: () => (
<div className="grid grid-cols-3 gap-4">
{(['primary', 'secondary', 'danger'] as const).map(variant => (
['small', 'medium', 'large'] as const).map(size => (
<div key={`${variant}-${size}`} className="flex flex-col gap-2">
<Button variant={variant} size={size}>Normal</Button>
<Button variant={variant} size={size} disabled>Disabled</Button>
<Button variant={variant} size={size} loading>Loading</Button>
</div>
))
))}
</div>
),
};This story file creates an interactive documentation page showing every possible Button state. Designers can review it to confirm the implementation matches Figma. Developers can reference it when using the component.
Pro tip: Use Storybook's autodocs feature to automatically generate prop documentation from TypeScript types. This keeps your documentation synchronized with your code.
Automating Component Handoff
Manual component handoff is time-consuming and error-prone. Here are tools that automate parts of the process:
Figma Plugins
Figma to Code plugins (like html.to.design, Quest, Anima) attempt to generate React components directly from Figma. Results are mixed—they work well for simple components but struggle with complex layouts and custom logic.
Better approach: Use plugins to extract design tokens (design tokens are the foundation), then manually build components that consume those tokens. This gives you clean, maintainable code rather than auto-generated spaghetti.
Design Token Pipelines
Tools like Style Dictionary transform design tokens from Figma into CSS variables, JavaScript constants, and platform-specific formats. Your components then reference these tokens instead of hard-coded values.
Example: Instead of bg-blue-600 in your Button, use bg-primary which maps to a token. When the primary color changes in Figma, you update the token file and all components automatically reflect the change.
See our Style Dictionary guide for complete setup instructions.
HandoffPro Approach
HandoffPro takes a different approach: it doesn't try to generate code automatically. Instead, it generates a structured JSONC brief that documents component specifications in a format AI coding assistants can understand.
For component library handoff, you'd:
- Screenshot your Figma component in all its variants
- Upload to HandoffPro
- Get a JSONC brief with extracted tokens, props, and state specifications
- Paste the brief into Cursor or Claude Code
- AI assistant generates the React component matching the spec
This gives you the speed of automation with the quality of hand-written code.
Component Library Handoff Checklist
Before you consider your component library handoff complete, verify:
- Every Figma component has exactly one corresponding code component (1:1 mapping)
- Component names match between Figma and code
- All Figma variant properties are implemented as typed props
- All component states (default, hover, focus, disabled, loading, error) are implemented
- Components use design tokens for colors, spacing, and typography (not hard-coded values)
- Storybook or similar documentation shows all component states and variants
- TypeScript types prevent invalid prop combinations
- Accessibility requirements (ARIA labels, keyboard navigation) are implemented
- Components are tested across all browsers and devices
- Design and engineering teams both reference the same component documentation
FAQ
Q: How do you map Figma components to React components?
A: Use 1:1 mapping where each Figma component corresponds to exactly one React component. Figma variants become component props, variant properties become prop types, and component instances become JSX elements. For example, a Figma Button with variants for size (small/medium/large) and variant (primary/secondary) maps to a React Button component with size and variant props.
Q: What naming convention should I use for design system components?
A: Use PascalCase for component names (Button, not button) and match Figma layer names exactly when possible. For variants, use prop-based naming: Button with variant='primary' instead of PrimaryButton. This keeps your component API flexible and prevents component explosion as you add more variants.
Q: How do you document all component states for developers?
A: Document each component with default, hover, focus, active, disabled, loading, and error states. Use tools like Storybook to create interactive documentation showing all state combinations. Include TypeScript types for props, default values, and examples of common usage patterns. This prevents developers from missing edge cases during implementation.