Consolidating Props
Figma often requires two component properties to control what a single element does: one to show or hide it, and another to set its content. Specs detects this pattern and merges the two into one nullable prop in the spec output, producing a cleaner API that matches how developers think about components.
The Problem
Figma’s component property system forces two separate properties for what developers think of as a single concern.
Text
Consider a Button with an optional label. In Figma, you need:
- Show Label (boolean) — toggles the text layer’s visibility
- Label (text) — sets the text content
From a developer’s perspective, this is one concern: the label is either present with a value, or it’s absent. A coded component would express this as a single label prop that accepts a string or null. But Figma’s property system forces it into two separate bindings.
Icon Glyph
The same pattern appears with icons. An icon element needs:
- Show Icon (boolean) — toggles the instance layer’s visibility
- Icon (instance swap) — selects which icon to display
Developers think of this as one thing: an icon is either provided or it isn’t. When the icon is an instance swap that matches a glyph name pattern, Specs classifies it as a glyph content type — but the two-prop problem remains the same.
When these prop pairs aren’t consolidated, consumers of the spec must manually understand which booleans correspond to which content props and how they interact — duplicating logic that Specs can infer automatically.
What It Does
Specs scans every element in every variant looking for binding pairs: an element that has both a visible binding (to a boolean prop) and a content binding (to a text, instance swap, or slot prop) on the same element. When a consistent pair is found across all variants, Specs:
- Merges the two props into one — the content prop becomes
nullable: true, meaning it accepts its normal values ornull - Sets the default — if the boolean defaults to
false(element hidden), the merged prop defaults tonull - Removes the boolean — the visibility toggle is dropped from the props list since it’s now expressed through nullability
- Adds metadata — a
$extensions.com.figma.visibilityPropfield records the original boolean prop name, so consumers can trace the merge
Before (without consolidation)
props: showLabel: type: boolean default: true label: type: string default: "Button" showIcon: type: boolean default: false icon: type: string contentType: glyph default: "IconPlus"A consumer must know that showLabel controls whether label matters, and showIcon controls whether icon matters.
After (with consolidation)
props: label: type: string nullable: true default: "Button" $extensions: com.figma: visibilityProp: showLabel icon: type: string contentType: glyph nullable: true default: null $extensions: com.figma: visibilityProp: showIconNow the intent is clear: label is a string that defaults to "Button" and can be set to null to hide the label. icon defaults to null (hidden) and can be set to a glyph name to show an icon.
How It’s Detected
The consolidation runs automatically — there’s no configuration needed. The engine validates pairs using these rules:
- Co-occurrence — the boolean’s
visiblebinding and the content prop’s binding must appear on the same element - One-to-one — each boolean must control exactly one element (a boolean bound to multiple elements’ visibility is not a clean pair)
- Consistent across variants — every variant where the element has a visibility binding must also have the content binding
- Stable prop names — the boolean and content prop names must be the same across all variants
If any rule fails, the props remain separate — no data is lost.
What Gets Paired
The engine recognizes three types of content bindings:
| Figma binding | Content type | Spec prop type | Example |
|---|---|---|---|
Text (characters) | string | StringProp | Label text, placeholder |
Instance swap (mainComponent) | glyph | StringProp with contentType: glyph | Icon selection |
Slot (slotContentId) | slot | SlotProp | Nested component |
Conditional Visibility in Variants
When a pair is active, the element’s visibility is no longer a bare true or false. Instead, the spec expresses it as a conditional — a structured expression that says “this element is visible when the prop is not null.” This connects the element-level rendering decision back to the prop API.
For example, a Button’s default variant with a consolidated label and icon:
default: anatomy: label: styles: visible: if: condition: operation: isNotNull args: value: $bind: label then: true else: false characters: "Button" iconGlyph: styles: visible: if: condition: operation: isNotNull args: value: $bind: icon then: true else: false mainComponent: "IconPlus"Rather than requiring consumers to cross-reference showLabel: true with a separate boolean prop, the conditional makes the relationship explicit: the Label element is visible when label is not null, and hidden when it is. The same applies to the icon glyph — its visibility is tied directly to whether the icon prop has a value.
See Also
- Icon Glyphs guide — How icon instances are classified as glyphs
- Schema: Conditional — Conditional expression syntax
- Schema: Props — Prop types and nullability
- Schema: Elements — Element visibility bindings