Custom Components for React
This guide explains how to configure custom React components in Locofy using the locofy.config.json file. This allows you to map your existing React components to Figma designs, enabling UIPro to generate code using your actual component library.
Configuration File: locofy.config.json
The locofy.config.json file is the central configuration that defines how your React components map to Figma components. It's automatically generated by UIPro when you run: "Create UIPro config for all components"
File Location
The file should be placed in your project root directory:
your-project/
├── src/
│ └── components/
├── locofy.config.json ← Configuration file
└── package.jsonReact Component Examples
Example 1: Button Component
Component Code:
import React from 'react';
interface ButtonProps {
label: string;
size?: 'small' | 'medium' | 'large';
variant?: 'primary' | 'secondary';
disabled?: boolean;
onClick?: () => void;
}
const Button: React.FC<ButtonProps> = ({
label,
size = 'medium',
variant = 'primary',
disabled = false,
onClick
}) => {
return (
<button
className={`btn btn-${size} btn-${variant}`}
disabled={disabled}
onClick={onClick}
>
{label}
</button>
);
};
export default Button;locofy.config.json Configuration:
{
"components": [
{
"path": "./src/components/Button.tsx",
"name": "Button",
"props": [
{
"name": "label",
"dataType": "string",
"propType": 1,
"isOptional": false,
"config": {
"layerName": "[children]"
}
},
{
"name": "size",
"dataType": "enum",
"propType": 1,
"isOptional": true,
"expectValues": ["small", "medium", "large"],
"config": {
"layerProp": "Size"
}
},
{
"name": "variant",
"dataType": "enum",
"propType": 1,
"isOptional": true,
"expectValues": ["primary", "secondary"],
"config": {
"layerProp": "Variant"
}
},
{
"name": "disabled",
"dataType": "boolean",
"propType": 4,
"isOptional": true
}
],
"config": {
"layerName": "Button"
}
}
],
"projectId": "your-project-id",
"projectName": "Your React App"
}Example 2: Card Component
Component Code:
import React, { ReactNode } from 'react';
interface CardProps {
header?: ReactNode;
children: ReactNode;
footer?: ReactNode;
className?: string;
}
const Card: React.FC<CardProps> = ({ header, children, footer, className }) => {
return (
<div className={`card ${className || ''}`}>
{header && <div className="card-header">{header}</div>}
<div className="card-content">{children}</div>
{footer && <div className="card-footer">{footer}</div>}
</div>
);
};
export default Card;locofy.config.json Configuration:
{
"components": [
{
"path": "./src/components/Card.tsx",
"name": "Card",
"props": [
{
"name": "header",
"dataType": "node",
"propType": 5,
"isOptional": true,
"config": {
"layerName": "Header"
}
},
{
"name": "children",
"dataType": "node",
"propType": 5,
"isOptional": false,
"config": {
"layerName": "Content"
}
},
{
"name": "footer",
"dataType": "node",
"propType": 5,
"isOptional": true,
"config": {
"layerName": "Footer"
}
},
{
"name": "className",
"dataType": "string",
"propType": 1,
"isOptional": true
}
],
"config": {
"layerName": "Card"
}
}
]
}Example 3: Input Component with Value Mapping
Component Code:
import React from 'react';
interface InputProps {
type?: 'text' | 'email' | 'password' | 'number';
placeholder?: string;
value?: string;
disabled?: boolean;
size?: 'sm' | 'md' | 'lg';
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
const Input: React.FC<InputProps> = ({
type = 'text',
placeholder,
value,
disabled = false,
size = 'md',
onChange
}) => {
return (
<input
type={type}
placeholder={placeholder}
value={value}
disabled={disabled}
className={`input input-${size}`}
onChange={onChange}
/>
);
};
export default Input;locofy.config.json Configuration:
{
"components": [
{
"path": "./src/components/Input.tsx",
"name": "Input",
"props": [
{
"name": "type",
"dataType": "enum",
"propType": 1,
"isOptional": true,
"expectValues": ["text", "email", "password", "number"]
},
{
"name": "placeholder",
"dataType": "string",
"propType": 1,
"isOptional": true
},
{
"name": "size",
"dataType": "enum",
"propType": 1,
"isOptional": true,
"expectValues": ["sm", "md", "lg"],
"config": {
"layerProp": "Size",
"valueMapping": {
"sm": ["small"],
"md": ["medium"],
"lg": ["large"]
}
}
},
{
"name": "disabled",
"dataType": "boolean",
"propType": 4,
"isOptional": true
}
],
"config": {
"layerName": "Input"
}
}
]
}Example 4: Avatar Component
Component Code:
import React from 'react';
interface AvatarProps {
src: string;
alt?: string;
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
shape?: 'circle' | 'square';
}
const Avatar: React.FC<AvatarProps> = ({
src,
alt = 'Avatar',
size = 'md',
shape = 'circle'
}) => {
return (
<img
src={src}
alt={alt}
className={`avatar avatar-${size} avatar-${shape}`}
/>
);
};
export default Avatar;locofy.config.json Configuration:
{
"components": [
{
"path": "./src/components/Avatar.tsx",
"name": "Avatar",
"props": [
{
"name": "src",
"dataType": "string",
"propType": 1,
"isOptional": false,
"attr": "src"
},
{
"name": "alt",
"dataType": "string",
"propType": 1,
"isOptional": true,
"attr": "alt"
},
{
"name": "size",
"dataType": "enum",
"propType": 1,
"isOptional": true,
"expectValues": ["xs", "sm", "md", "lg", "xl"],
"config": {
"layerProp": "Size"
}
},
{
"name": "shape",
"dataType": "enum",
"propType": 1,
"isOptional": true,
"expectValues": ["circle", "square"],
"config": {
"layerProp": "Shape"
}
}
],
"config": {
"layerName": "Avatar"
}
}
]
}Example 5: Icon Button Component
Component Code:
import React, { ReactNode } from 'react';
interface IconButtonProps {
icon: ReactNode;
label: string;
size?: 'small' | 'medium' | 'large';
onClick?: () => void;
}
const IconButton: React.FC<IconButtonProps> = ({
icon,
label,
size = 'medium',
onClick
}) => {
return (
<button className={`icon-btn icon-btn-${size}`} onClick={onClick}>
<span className="icon-btn-icon">{icon}</span>
<span className="icon-btn-label">{label}</span>
</button>
);
};
export default IconButton;locofy.config.json Configuration:
{
"components": [
{
"path": "./src/components/IconButton.tsx",
"name": "IconButton",
"props": [
{
"name": "icon",
"dataType": "node",
"propType": 5,
"isOptional": false,
"config": {
"layerName": "[icon]"
}
},
{
"name": "label",
"dataType": "string",
"propType": 1,
"isOptional": false,
"config": {
"layerName": "[label]"
}
},
{
"name": "size",
"dataType": "enum",
"propType": 1,
"isOptional": true,
"expectValues": ["small", "medium", "large"]
}
],
"config": {
"layerName": "IconButton"
}
}
]
}Complete locofy.config.json Example
Here's a complete configuration file with multiple components:
{
"components": [
{
"path": "./src/components/Button.tsx",
"name": "Button",
"props": [
{
"name": "label",
"dataType": "string",
"propType": 1,
"isOptional": false,
"config": {
"layerName": "[children]"
}
},
{
"name": "size",
"dataType": "enum",
"propType": 1,
"isOptional": true,
"expectValues": ["small", "medium", "large"],
"config": {
"layerProp": "Size"
}
},
{
"name": "variant",
"dataType": "enum",
"propType": 1,
"isOptional": true,
"expectValues": ["primary", "secondary"],
"config": {
"layerProp": "Variant"
}
}
],
"config": {
"layerName": "Button"
}
},
{
"path": "./src/components/Card.tsx",
"name": "Card",
"props": [
{
"name": "header",
"dataType": "node",
"propType": 5,
"isOptional": true,
"config": {
"layerName": "Header"
}
},
{
"name": "children",
"dataType": "node",
"propType": 5,
"isOptional": false,
"config": {
"layerName": "Content"
}
},
{
"name": "footer",
"dataType": "node",
"propType": 5,
"isOptional": true,
"config": {
"layerName": "Footer"
}
}
],
"config": {
"layerName": "Card"
}
},
{
"path": "./src/components/Input.tsx",
"name": "Input",
"props": [
{
"name": "placeholder",
"dataType": "string",
"propType": 1,
"isOptional": true
},
{
"name": "size",
"dataType": "enum",
"propType": 1,
"isOptional": true,
"expectValues": ["sm", "md", "lg"],
"config": {
"layerProp": "Size",
"valueMapping": {
"sm": ["small"],
"md": ["medium"],
"lg": ["large"]
}
}
}
],
"config": {
"layerName": "Input"
}
}
],
"projectId": "abc123xyz",
"projectName": "My React App"
}Configuration Properties Reference
Component-Level Properties
| Property | Type | Description |
|---|---|---|
path | string | Relative path to component file from project root |
name | string | Component function/class name |
props | array | Array of prop definitions |
config | object | Component-level configuration |
targetTags | array | Replace native HTML tags with this component |
acceptAllStyles | boolean | Accept all generated styles |
wrapperComponent | object | Define wrapper component |
subComponents | array | Configure child components |
siblingComponents | array | Add sibling components |
Prop Properties
| Property | Type | Description |
|---|---|---|
name | string | Prop name |
dataType | string | Data type: string, number, boolean, enum, node, object |
propType | number | 1 for most types, 4 for boolean, 5 for node |
isOptional | boolean | Whether prop is optional |
expectValues | array | Allowed values for enum types |
attr | string | HTML attribute to bind to (e.g., src, alt) |
config | object | Prop-level mapping configuration |
Config Object Properties
| Property | Type | Description |
|---|---|---|
layerName | string | Figma layer name to map from |
layerProp | string | Figma component property name |
valueMapping | object | Map code values to Figma values |
layer | array | Advanced layer configuration |
nodeField | string | Extract from node field (e.g., name) |
Advanced Features
1. Target Tags
Replace native HTML elements with your custom component:
{
"components": [
{
"path": "./src/components/Box.tsx",
"name": "Box",
"targetTags": ["div", "section"],
"props": []
}
]
}2. Wrapper Component
Define a parent wrapper that should contain your component:
{
"components": [
{
"path": "./src/components/Input.tsx",
"name": "Input",
"wrapperComponent": {
"component": "FormField",
"props": [
{
"name": "label",
"config": {
"layerName": "Label"
}
}
]
},
"props": []
}
]
}3. Sub Components
Configure child components within a parent:
{
"components": [
{
"path": "./src/components/FormField.tsx",
"name": "FormField",
"subComponents": [
{
"component": "Input",
"props": [
{
"name": "maxLength",
"config": {
"layerProp": "Character Limit"
}
}
]
}
],
"props": []
}
]
}4. Sibling Components
Add components that render alongside your main component:
{
"components": [
{
"path": "./src/components/Button.tsx",
"name": "Button",
"siblingComponents": [
{
"component": "Icon",
"position": "before",
"config": {
"layerProp": "Icon"
},
"props": [
{
"name": "name",
"config": {
"layerProp": "Icon"
}
}
]
}
],
"props": []
}
]
}5. Component Variant Matching
Map different components based on Figma property values:
{
"components": [
{
"path": "./src/components/PrimaryButton.tsx",
"name": "PrimaryButton",
"config": {
"layerName": "Button",
"layerProps": {
"variant": "primary"
}
},
"props": []
},
{
"path": "./src/components/SecondaryButton.tsx",
"name": "SecondaryButton",
"config": {
"layerName": "Button",
"layerProps": {
"variant": "secondary"
}
},
"props": []
}
]
}Workflow
- Create your React components with proper TypeScript interfaces
- Run UIPro configuration in your IDE:
"Create UIPro config for all components" - UIPro generates
locofy.config.jsonautomatically - Review and adjust the configuration as needed
- Open Figma and use the UIPro plugin
- Map components in the UIPro interface
- Generate code and UIPro will use your custom components
Best Practices
- ✅ Use TypeScript for better type inference
- ✅ Keep component and prop names consistent between Figma and code
- ✅ Use
valueMappingwhen Figma property names differ from code - ✅ Set
isOptional: falsefor required props - ✅ Use
attrfor HTML attributes likesrc,alt,href - ✅ Use descriptive
layerNamevalues in config - ✅ Organize components in a clear folder structure
- ✅ Document complex mappings with comments
Troubleshooting
Component not detected
- Verify the file path in
locofy.config.jsonis correct - Ensure the component name matches the export
- Check that the component is properly exported
Props not mapping correctly
- Check
config.layerPropmatches Figma property name exactly - Verify
valueMappingif using different value names - Ensure
dataTypematches the actual prop type - Check
propTypeis set correctly (1, 4, or 5)
Config file not being used
- Ensure
locofy.config.jsonis in project root - Restart your IDE after creating the config
- Verify JSON syntax is valid
Value mapping issues
- Check that all mapped values exist in Figma
- Verify the mapping direction (code → Figma)
- Ensure enum
expectValuesincludes all mapped values
Next Steps
- Check the main Custom Components guide for general concepts
- Explore other framework guides: Angular, Vue, React Native
- Join our Discord community (opens in a new tab) for support