This article records how I currently translate Figma component properties into runtime component APIs.
Figma variants, booleans, and INSTANCE_SWAP properties are designed to help designers preview, maintain, and reuse components inside the editor. Code components have a different job: they need to represent real interaction state, valid combinations, and a clear public API.
I no longer map every Figma property directly to a prop. I first check what that property means in the actual product.
A common Figma pattern is a Status variant with values such as:
Default
Hover
Focus
SelectedThis is useful for visual comparison in the design file. In code, native browser states can usually be handled directly with CSS:
:hover
:focus-visible
:disabledFor a List - Select component, I would map the states like this: Hover → :hover; Focus → :focus-visible; Selected → a real selected prop.
Selected still needs application state because the browser does not know which item the product considers selected.
The variant label itself is not enough to decide the mapping. In one Select component, a Figma state named Focus actually represented an open dropdown panel. That state belongs to an open-style runtime state rather than :focus-visible.
Before mapping a state, I check: what causes it; whether the browser already knows about it; whether it represents focus, selection, expansion, or another product state.
Figma INSTANCE_SWAP properties allow designers to replace an icon or nested component inside an instance.
For example, a Leading Icon property in Figma may simply become:
<ListSelect leadingIcon={<SearchIcon />} />The code component does not need to reproduce Figma's instance-swapping mechanism. It only needs to preserve the runtime capability: a certain position can receive an icon or child component.
The exact API can use a React element, component type, or a more general slot pattern depending on the codebase.
Figma variant axes can become expensive to maintain as combinations grow. Designers may bundle several changes into one variant to keep the component set manageable.
Group - Input with Btn was one example. Figma exposed a single Type axis:
Normal
IconThat value changed two separate things: whether the content area was editable, and whether the button used text or an icon.
These concerns do not have to stay coupled at runtime. A code API can expose them separately:
contentMode="editable | readonly"
buttonMode="text | icon"This supports all valid combinations instead of only the combinations already drawn in Figma.
When a variant changes multiple unrelated aspects at once, I check whether those aspects really need to move together in the product. If they can vary independently, the code API should usually expose them independently.
Boolean properties can be shared across an entire Figma component set, while individual variants may connect that boolean to different layers.
A FormField component had a Show Required property with two different meanings: Input / Textarea variants show a required asterisk; the Tag variant shows an add-member action.
A code prop such as:
showRequired={true}would be misleading for the Tag variant.
I now inspect what each shared boolean actually controls in every relevant variant. If the meaning changes, common options include: splitting it into more specific props; limiting the prop to certain variants; splitting the component when the variants already represent different component responsibilities.
Runtime prop names should keep a stable meaning even when the original Figma component set uses a shared property name.
Figma often lays out several combinations so designers can compare them visually. Code still needs to determine which states can coexist.
If only one value can be active at a time, an enum is usually appropriate:
size="sm | md | lg"
orientation="horizontal | vertical"If several states can coexist, separate booleans are clearer:
selected
highlighted
removableThe reverse matters too. Three mutually exclusive runtime states should not become three booleans such as isA, isB, and isC, because that API allows invalid combinations.
I usually write down which states can exist at the same time before deciding the final prop shape.
Documentation often needs static previews of Hover or Focus states.
The production component can continue using native CSS states, while Storybook adds demo-only controls such as:
forceHover
forceFocusThese controls stay inside the documentation layer: they are not part of the normal public API; they do not appear in the real product Playground; they do not participate in product state management.
This keeps visual comparison easy without turning presentation-only states into permanent component props.
When I receive a new Figma component set, I usually review it in this order.
| Figma property | Typical code treatment |
|---|---|
| Hover / Focus / Disabled variants | Native CSS / DOM state |
| Product Selected / Open state | Boolean or controlled state prop |
| Mutually exclusive variants | Enum prop |
| Composable states | Independent boolean props |
| INSTANCE_SWAP | Icon / slot / child component |
| Variant bundling several concerns | Split by runtime semantics |
| Shared boolean with inconsistent meaning | Split the prop, limit its scope, or split the component |
| Static Hover / Focus preview | Storybook-only force state |
Figma and code describe the same product components in different working environments. Figma needs efficient visual maintenance and comparison; code needs clear state ownership, valid combinations, and a stable API.
I now treat Figma properties as design-side input and derive the final code props from runtime semantics. This produces component APIs that match actual product behavior more closely and avoids carrying editor-specific structure into production code.