Instances
This article describes the generated code for instances of components, how overrides are applied and the principles we follow.
Any question about the design decisions? Anything not matching your project practices? Please let us know! we may add an option for the flexibility you need.
At Clapy, we are proud to be the first tool generating code from Figma with instances of components and overrides. We consider customizable components to be the bottom line of production-ready code.
For now, component overrides are only available in React.
If a frame contains multiple instances of a Button component, with customizations done on some of them (color, text…), we generate a single Button component. Instances refer to this component, passing properties that apply the customizations, we call overrides in this article.
Figma allows 4 different overrides:
- Styles (fill, stroke, layout…)
- Text
- Swap instances
- Show or hide
All those overrides are passed to the instance as properties.
The style changes are applied with classes. The system is inspired by MUI: the className property applies to the root node, and sub-nodes are restyled using a
classes
property. Classes are passed down to the target node.Example:
interface Props {
className?: string;
classes?: {
root?: string;
btnTxt?: string;
};
}
export const Button: FC<Props> = memo(function Button(props = {}) {
return (
<button className={`${classes.root} ${props.classes?.root || ''} ${props.className || ''}`}>
<div className={props.classes?.btnTxt}>Click</div>
</button>
);
});
Usage:
<Button
className={classes.newRoot}
classes={{
btnTxt: classes.newTextStyle
}} />
To ensure instance styles override the component styles, the instance selectors are doubled. Example:
.label.label {
/* ... */
}
We opted for this solution because we use CSS modules. A downside of it is that, in the selector, we cannot refer to classes defined in a separate file.
The root node can be overridden with both
className
and classes.root
. This is an inspiration from MUI, with the benefits:- Developers are used to the
className
property that would apply their style to the parent node, - Not being able to override the root node inside
classes
would be weird, so we keep it for consistency.
If the text changes in an instance, the whole new text is passed as a property (
text
). Figma gives a lot of flexibility on what can be changed in the text. We keep this flexibility in the generated code.Example:
interface Props {
text?: {
btnTxt?: ReactNode;
};
}
export const Button: FC<Props> = memo(function Button(props = {}) {
return (
<button>
{props.text?.btnTxt != null
? props.text?.btnTxt
: <div className={classes.btnTxt}>Click me!</div>}
</button>
);
});
Usage:
<Button text={{
btnTxt: <div className={classes.newTxtStyle}>New text</div>
}} />
This refers to a Figma feature: replace an instance of component A with an instance of component B, putting it where the instance of A was, even if it’s inside another instance.
In React, we use render props to replicate this behavior, i.e. instances of sub-components passed as properties to apply the
swap
.This feature is particularly interesting in Figma when you have an instance containing other instances. The latter can be swapped with your own components to to put arbitrary content in the original component.
Example:
interface Props {
swap?: {
btnArrow?: ReactNode;
};
}
export const Button: FC<Props> = memo(function Button(props = {}) {
return (
<button>
{props.swap?.btnArrow || <ArrowComponent />}
<div className={classes.btnTxt}>Click me!</div>
</button>
);
});
Usage:
<Button swap={{
btnArrow: <StarComponent />
}} />
An instance can reveal a node that is hidden in the original component, or on the contrary, hide a node that is visible. The
hide
property applies the override.Example:
interface Props {
hide?: {
btnArrow?: boolean;
};
}
export const Button: FC<Props> = memo(function Button(props = {}) {
return (
<button>
{!props.hide?.btnArrow && <ArrowComponent />}
<div className={classes.btnTxt}>Click me!</div>
</button>
);
});
Usage:
<Button hide={{
btnArrow: true
}} />
When a Figma element exported as SVG is customized in an instance, Clapy exports the new SVG with overrides, and pass it to the instance leveraging the swap feature described above (render prop).
In case you wonder, we support sub-instances (instances containing instances). And as many layers of components as you wish.
In short, you should design on Figma showing examples of usages. E.g. if a list entry is supposed to have a dynamic texts, you should add 2+ entries with different texts.
Clapy will guess the properties from your examples.