Guidelines for writing Figma Code Connect property mappings. Use this skill when working on Figma Code Connect files, which typically end in .figma.tsx.
variant: figma.enum('variant', {
'Figma Display Name': 'codeValue',
'Primary': 'primary',
'Secondary': 'secondary',
}),disabled: figma.boolean('disabled'),
loading: figma.boolean('loading'),In some cases, you only want to render a certain prop if it matches some value in Figma. You can do this either by passing a partial mapping object, or setting the value to undefined.
// Don't render the prop if 'Has label' in figma is `false`
figma.boolean('has label', {
true: figma.string('label'),
false: undefined,
});label: figma.string('label'),A common pattern in Figma design systems is to override text content directly on instances rather than using component properties. Use figma.textContent() to extract the actual text from a named text layer.
// Extract text from a layer named 'Title'
title: figma.textContent('Title'),Key difference: Use figma.string() when text is controlled by a Figma component property. Use figma.textContent() when text lives as content in a text layer that designers override directly.
CRITICAL: figma.textContent() only works on actual TEXT layers — never on component instances.
Before using figma.textContent('LayerName'), always call get_metadata on that layer's node ID to verify its type. In the metadata response, the node must appear as a <text> element. If it appears as <instance>, <symbol>, <frame>, or any other non-text type, figma.textContent() will fail at runtime in Figma with a "Layer not found" error even though the layer name is correct.
When the target layer is a component instance (e.g. a reusable text component), use a hardcoded placeholder string instead:
// ❌ Wrong: 'string.label' is a component instance, not a text layer
label: figma.boolean('show label', {
true: figma.textContent('string.label'),
false: undefined,
}),
// ✅ Correct: use a placeholder string when the layer is an instance
label: figma.boolean('show label', {
true: 'Your label here.',
false: undefined,
}),Use figma.instance() returns the JSX from another figma.connect() call that you can use in the example. This is useful for components that accept a node of another React component as a prop.
In the example below, Button accepts an instance of Icon as the icon prop.
We would need to have another call to figma.connect() for the Icon component somewhere in our code connect setup.
figma.connect(Button, 'https://...', {
props: {
icon: figma.instance('Icon'),
},
example: ({ icon }) => {
return <Button icon={icon}>Instance prop Example</Button>;
},
});Use this property mapping when your React component accepts children. figma.children maps a Figma layer name to the children prop.
// Maps child instances that aren't bound to an instance-swap prop
icon: figma.children('IconLayer'),// Access properties from a nested instance layer named 'Avatar'
avatar: figma.nestedProps('Avatar', {
size: figma.enum('size', { ... }),
src: figma.string('src'),
}),
// In example: use avatar.size, avatar.srcIn Figma's properties panel, you may see properties with the ↳ symbol (e.g., ↳ subtitle). This indicates the property is exposed from a child layer, not defined directly on the parent component.
Why this matters: The Code Connect validation run during figma connect publish has limited coverage. It only validates these prop kinds:
figma.boolean(), figma.enum(), figma.string() - validates the property name existsfigma.children() - validates the layer name existsThese prop kinds are NOT validated at all:
figma.nestedProps() - layer name and inner property mappings are not checkedfigma.instance() - layer/instance name is not checkedfigma.textContent() - layer name is not checkedAdditionally, validation does not recurse into boolean true/false branch values.
This can result in technically incorrect mappings being published to Figma withoug being caught during validation.
Incorrect approach (will pass validation but fail at runtime):
// ❌ Wrong: 'subtitle' should be a nested property, not a direct component property
subtitle: figma.boolean('show subtitle', {
true: figma.string('subtitle'),
false: undefined,
}),Correct approach using figma.nestedProps():
// ✅ Correct: Use nestedProps to access properties from the child layer
subtitle: figma.boolean('show subtitle', {
true: figma.nestedProps('subtitle', {
text: figma.string('subtitle'),
}),
false: { text: undefined },
}),
// In example: use subtitle.textTip: When in doubt about whether a property is direct or nested, check if it has the ↳ symbol in Figma's properties panel. If it does, you likely need figma.nestedProps() or figma.textContent().
For components with multiple variants in Figma, create separate figma.connect() calls:
// Default variant
figma.connect(ComponentName, 'figma-url', {
/* props */
});
// Specific variant
figma.connect(ComponentName, 'figma-url', {
variant: { 'show suffix': true },
props: {
/* variant-specific props */
},
example: (props) => <ComponentName {...props} />,
});Use variant-specific connects when child layer names differ across variants.
figma.children('LayerName') only matches layers with that exact name. If different variants use different layer names for the same logical prop (e.g. Button for the single-action variant and ButtonGroup for the multi-action variant), a single figma.children() call will silently produce nothing for the non-matching variants. Split into separate connects with variant: { ... } filters:
// ❌ Wrong: 'ButtonGroup' doesn't exist in the single-action variant
figma.connect(Footer, url, {
props: { action: figma.children('ButtonGroup') },
example: ({ action }) => <Footer action={action} />,
});
// ✅ Correct: separate connects for each variant
figma.connect(Footer, url, {
variant: { '# of actions': '1' },
props: { action: figma.children('Button') },
example: ({ action }) => <Footer action={action} />,
});
figma.connect(Footer, url, {
variant: { '# of actions': '2' },
props: { action: figma.children('ButtonGroup') },
example: ({ action }) => <Footer action={action} />,
});Problem: Using figma.string() when the text is a layer name, not a property.
// ❌ Wrong: 'value' is a text layer name, not a property
children: figma.string('value');
// ✅ Correct: Use textContent for text layers
children: figma.textContent('value');Problem: Treating an enum property value as a separate property.
// ❌ Wrong: 'disabled' might be a value of 'state', not its own property
disabled: figma.boolean('disabled');
// ✅ Correct: Map from the state enum
disabled: figma.enum('state', {
disabled: true,
default: false,
focused: false,
hovered: false,
pressed: false,
});figma.textContent() on a Component InstanceProblem: figma.textContent() is called with a layer name that belongs to a component instance, not a raw text layer. The mapping appears valid but fails at runtime in Figma with "Layer not found".
How to detect: Call get_metadata on the layer node ID. If the response shows <instance ...> instead of <text ...>, figma.textContent() will not work.
// ❌ Wrong: 'string.label' is a component instance — textContent silently fails
label: figma.boolean('show label', {
true: figma.textContent('string.label'),
false: undefined,
}),
// ✅ Correct: use a hardcoded placeholder string
label: figma.boolean('show label', {
true: 'Your label here.',
false: undefined,
}),Problem: Property names in Figma often have spaces and must match exactly.
// ❌ Wrong: camelCase doesn't match Figma property name
showStart: figma.boolean('showStart');
// ✅ Correct: Use exact Figma property name with spaces
start: figma.boolean('show start', {
true: figma.instance('start'),
false: undefined,
});f79a780
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.