Code Connect (.figma.tsx)
Lumen maps Figma components to real code with parser-based Code Connect:
Component.figma.tsx files co-located with each component, using
figma.connect() from @figma/code-connect. The Figma CLI parses these files
and publishes the snippets to Figma Dev Mode (CI:
.github/workflows/figma-code-connect.yml).
The whole codebase uses parser-based
.figma.tsxfiles (~90 of them acrosslibs/ui-reactandlibs/ui-rnative). There are no.figma.tsMCP template files here — don't author that style.
Where files live & config
- Co-locate as
Component.figma.tsxinside the component folder (e.g.libs/ui-rnative/src/lib/Components/core/Tag/Tag.figma.tsx). - Only
libs/ui-reactandlibs/ui-rnativehave afigma.config.json(with acodeConnectblock:includeglobs,label(e.g."React Native"), andinteractiveSetupFigmaFileUrl). The visualization libs have no Code Connect coverage. Read the config to confirm the include path — there is noparser/paths/importPathskey.
File structure
import figma from '@figma/code-connect';
import { Placeholder } from '../../symbols';
import { Tag } from './Tag';
figma.connect(Tag, 'https://www.figma.com/design/<fileKey>?node-id=<node-id>', {
imports: ["import { Tag } from '@ledgerhq/lumen-ui-rnative'"],
props: {
label: figma.string('label'),
appearance: figma.enum('appearance', { base: 'base', gray: 'gray' /* …every value */ }),
icon: figma.boolean('show-icon', { true: Placeholder, false: undefined }),
},
example: (props) => (
<Tag label={props.label} appearance={props.appearance} icon={props.icon} />
),
});
figma.connect(Component, url, opts)is the usual form. You can also callfigma.connect(url, opts)without a component to map another Figma node to a different example of the same code component.- Multiple
figma.connect()calls per file are normal — a compound component maps each part in one file (e.g.Card.figma.tsxconnectsCard,CardHeader, … each with its ownexample). importslists the import statements a consumer needs, written with the published package specifier (@ledgerhq/lumen-ui-react,@ledgerhq/lumen-ui-rnative/symbols,@ledgerhq/crypto-icons) — never relative paths.
Mapping Figma props → code (props)
| Figma property | Helper | Notes |
|---|---|---|
| text | figma.string('name') |
|
| variant / enum | figma.enum('name', { FigmaValue: codeValue }) |
map every value — an unmapped value returns undefined |
| boolean | figma.boolean('name', { true: X, false: undefined }) |
undefined omits the prop entirely |
| nested child | figma.children('.layer-name') |
pulls a connected child layer into the example |
| swapped instance | figma.instance('name') |
A single Figma property can drive two code props — e.g. map the visible variant
and derive disabled from it: disabled: figma.enum('state', { disabled: true }).
Best practices
These are verified against the real files — follow them so the published snippets stay readable and prop-driven.
1. Keep example a flat render of props. All conditional logic, branching,
and value derivation belongs in props, never in example.
// ❌ BAD — conditional rendering in example
example: (props) => (
<ListItem>
{props.leadingContent === 'spot' && <Spot size={props.leadingContentSize} />}
{props.leadingContent === 'interface-icon' && <Placeholder size={24} />}
</ListItem>
)
// ✅ GOOD — example is a flat render, all logic is in props
example: (props) => <ListItem>{props.leadingContent}</ListItem>
2. Nest figma.enum / figma.boolean to encode multi-axis logic. When a
prop's JSX depends on more than one Figma property, nest the mapping calls
instead of branching in example.
leadingContent: figma.enum('leading-content', {
'no-icon': undefined,
spot: figma.enum('size', {
md: <Spot size={48} appearance='icon' icon={Settings} />,
sm: <Spot size={24} appearance='icon' icon={Settings} />,
}),
'interface-icon': <Placeholder size={24} />,
}),
3. Use a separate numeric prop when a size drives a third-party component. Derive it as its own prop and use it inline rather than branching.
tagSize: figma.enum('size', { md: 16, sm: 12 }),
example: (props) => <MediaTag leadingContent={<CryptoIcon size={props.tagSize} />} />,
4. Use a typed placeholder for un-mappable props rather than inventing a
mapping — e.g. example: (props: Omit<TagProps, 'icon'> & { icon: any }) => …
or size={'<insert-size>' as any}.
5. Never invent code props. Every attribute in example must exist on the
component's real props interface. If a Figma property has no code equivalent,
omit it.
Validate
- The Figma Code Connect CLI parses these files; run it locally to confirm a file parses before pushing.
- Publishing happens in CI via
.github/workflows/figma-code-connect.yml. - Note:
.figma.@(ts|tsx)files are inignorePatternsForPlanCheck(nx.json), so they don't require a version plan.
Review checks
Rules verifiable from a diff.
| Check | Applies to | Detect | Skip |
|---|---|---|---|
Conditional logic / branching in example instead of props |
.figma.tsx |
&&, ternaries, derivation inside example |
flat prop render |
figma.enum maps only some Figma values |
.figma.tsx |
enum with fewer cases than the Figma property | intentionally-omitted undefined mappings |
imports uses a relative path instead of the published specifier |
.figma.tsx |
'../…' in imports |
— |
example references a prop that isn't on the component |
.figma.tsx |
attribute not in the component's props | typed placeholder for un-mappable props |
.figma.tsx added for a lib without Code Connect |
ui-*-visualization |
new .figma.tsx under a visualization lib |
— |