💡 Summary
Figma-use allows users to control Figma via CLI for design automation and JSX rendering.
🎯 Target Audience
🤖 AI Roast: “Powerful, but the setup might scare off the impatient.”
Risk: Medium. Review: shell/CLI command execution; outbound network access (SSRF, data egress); filesystem read/write scope and path traversal; dependency pinning and supply-chain risk. Run with least privilege and audit before enabling in production.
name: figma-use description: Control Figma via CLI — create shapes, frames, text, components, set styles, layout, variables, export images. Use when asked to create/modify Figma designs or automate design tasks.
figma-use
CLI for Figma. Two modes: commands and JSX.
# Commands figma-use create frame --width 400 --height 300 --fill "#FFF" --layout VERTICAL --gap 16 figma-use create icon mdi:home --size 32 --color "#3B82F6" figma-use set fill 1:23 "$Colors/Primary" # JSX (props directly on elements, NOT style={{}}) echo '<Frame p={24} bg="#3B82F6" rounded={12}> <Text size={18} color="#FFF">Hello</Text> </Frame>' | figma-use render --stdin --x 100 --y 100
Before You Start
figma-use status # Check connection
If not connected — start Figma with remote debugging:
# macOS open -a Figma --args --remote-debugging-port=9222 # Windows "C:\Users\%USERNAME%\AppData\Local\Figma\Figma.exe" --remote-debugging-port=9222 # Linux figma --remote-debugging-port=9222
Start Figma with --remote-debugging-port=9222 and you're ready.
Two Modes
Imperative — single operations:
figma-use create frame --width 400 --height 300 --fill "#FFF" --radius 12 figma-use set fill <id> "#FF0000" figma-use node move <id> --x 100 --y 200
Declarative — render JSX trees:
echo '<Frame p={24} gap={16} flex="col" bg="#FFF" rounded={12}> <Text size={24} weight="bold" color="#000">Title</Text> <Text size={14} color="#666">Description</Text> </Frame>' | figma-use render --stdin --x 100 --y 200
stdin supports both pure JSX and full module syntax with imports:
import { Frame, Text, defineComponent } from 'figma-use/render' const Button = defineComponent( 'Button', <Frame bg="#3B82F6" p={12} rounded={6}> <Text color="#FFF">Click</Text> </Frame> ) export default () => ( <Frame flex="row" gap={8}> <Button /> <Button /> </Frame> )
Elements: Frame, Rectangle, Ellipse, Text, Line, Star, Polygon, Vector, Group, Icon, Image, Instance
Use <Instance> to create component instances:
<Frame flex="row" gap={8}> <Instance component="59763:10626" /> <Instance component="59763:10629" /> </Frame>
⚠️ Always use --x and --y to position renders. Don't stack everything at (0, 0).
Icons
150k+ icons from Iconify by name:
figma-use create icon mdi:home figma-use create icon lucide:star --size 48 --color "#F59E0B" figma-use create icon heroicons:bell-solid --component # as Figma component
In JSX:
<Icon name="mdi:home" size={24} color="#3B82F6" />
Images
Load images from URL:
<Image src="https://example.com/photo.jpg" w={200} h={150} />
Export JSX
Convert Figma nodes back to JSX code:
figma-use export jsx <id> # Minified figma-use export jsx <id> --pretty # Formatted # Format options figma-use export jsx <id> --pretty --semi --tabs # Match vector shapes to Iconify icons (requires: npm i whaticon) figma-use export jsx <id> --match-icons figma-use export jsx <id> --match-icons --icon-threshold 0.85 --prefer-icons lucide,tabler
Round-trip workflow:
# Export → edit → re-render figma-use export jsx <id> --pretty > component.tsx # ... edit the file ... figma-use render component.tsx --x 500 --y 0
Compare two nodes as JSX:
figma-use diff jsx <from-id> <to-id>
Export Storybook (Experimental)
Export all components on current page as Storybook stories:
figma-use export storybook # Output to ./stories/ figma-use export storybook --out ./src/stories # Custom output dir figma-use export storybook --match-icons # Match vectors to Iconify icons figma-use export storybook --no-semantic-html # Disable semantic HTML conversion
Semantic HTML: By default, components are converted to semantic HTML elements based on their names:
Input/*,TextField/*→<input type="text">Textarea/*→<textarea>Checkbox/*→<input type="checkbox">Radio/*→<input type="radio">Button/*→<button>Select/*,Dropdown/*→<select>
Use --no-semantic-html to disable this and keep <Frame> elements.
Generates .stories.tsx files:
- ComponentSets → React component with props + stories with args
- VARIANT properties → Union type props (
variant?: 'Primary' | 'Secondary') - TEXT properties → Editable string props (
label?: string) - Components grouped by
/prefix →Button/Primary,Button/Secondary→Button.stories.tsx
Example output for Button with variant and label:
// Button.tsx export interface ButtonProps { label?: string variant?: 'Primary' | 'Secondary' } export function Button({ label, variant }: ButtonProps) { if (variant === 'Primary') return ( <Frame> <Text>{label}</Text> </Frame> ) // ... } // Button.stories.tsx export const Primary: StoryObj<typeof Button> = { args: { label: 'Click', variant: 'Primary' } }
Variables as Tokens
Reference Figma variables in any color option with var:Name or $Name:
figma-use create rect --width 100 --height 100 --fill 'var:Colors/Primary' figma-use set fill <id> '$Brand/Accent'
In JSX:
<Frame bg="$Colors/Primary" /> <Text color="var:Text/Primary">Hello</Text>
Style Shorthands
Size & Position:
| Short | Full | Values |
|-------|------|--------|
| w, h | width, height | number or "fill" |
| minW, maxW | minWidth, maxWidth | number |
| minH, maxH | minHeight, maxHeight | number |
| x, y | position | number |
Layout:
| Short | Full | Values |
|-------|------|--------|
| flex | flexDirection | "row", "col" |
| gap | spacing | number |
| wrap | layoutWrap | true |
| justify | justifyContent | "start", "center", "end", "between" |
| items | alignItems | "start", "center", "end" |
| p, px, py | padding | number |
| pt, pr, pb, pl | padding sides | number |
| position | layoutPositioning | "absolute" |
| grow | layoutGrow | number |
| stretch | layoutAlign | true → STRETCH |
Appearance:
| Short | Full | Values |
|-------|------|--------|
| bg | fill | hex or $Variable |
| stroke | strokeColor | hex |
| strokeWidth | strokeWeight | number |
| strokeAlign | strokeAlign | "inside", "outside" |
| opacity | opacity | 0..1 |
| blendMode | blendMode | "multiply", etc. |
Corners:
| Short | Full | Values |
|-------|------|--------|
| rounded | cornerRadius | number |
| roundedTL/TR/BL/BR | individual corners | number |
| cornerSmoothing | squircle smoothing | 0..1 (iOS style) |
Effects:
| Short | Full | Values |
|-------|------|--------|
| shadow | dropShadow | "0px 4px 8px rgba(0,0,0,0.25)" |
| blur | layerBlur | number |
| overflow | clipsContent | "hidden" |
| rotate | rotation | degrees |
Text:
| Short | Full | Values |
|-------|------|--------|
| size | fontSize | number |
| weight | fontWeight | "bold", number |
| font | fontFamily | string |
| color | textColor | hex |
Grid (CSS Grid layout):
| Short | Full | Values |
|-------|------|--------|
| display | layoutMode | "grid" |
| cols | gridTemplateColumns | "100px 1fr auto" |
| rows | gridTemplateRows | "auto auto" |
| colGap | columnGap | number |
| rowGap | rowGap | number |
Components (via .figma.tsx)
First call creates master, rest create instances:
import { defineComponent, Frame, Text } from 'figma-use/render' const Card = defineComponent( 'Card', <Frame p={24} bg="#FFF" rounded={12}> <Text size={18} color="#000"> Card </Text> </Frame> ) export default () => ( <Frame gap={16} flex="row"> <Card /> <Card /> </Frame> )
figma-use render ./Card.figma.tsx --x 100 --y 200 figma-use render --examples # Full API reference
Variants (ComponentSet)
import { defineComponentSet, Frame, Text } from 'figma-use/render' const Button = defineComponentSet( 'Button', { variant: ['Primary', 'Secondary'] as const, size: ['Small', 'Large'] as const }, ({ variant, size }) => ( <Frame p={size === 'Large' ? 16 : 8} bg={variant === 'Primary' ? '#3B82F6' : '#E5E7EB'} rounded={8} > <Text color={variant === 'Primary' ? '#FFF' : '#111'}> {variant} {size} </Text> </Frame> ) )
Creates real ComponentSet with all combinations.
Diffs
Compare frames and generate patch:
figma-use diff create --from <id1> --to <id2>
--- /Card/Header #123:457 +++ /Card/Header #789:013 type: FRAME size: 200 50 -fill: #FFFFFF +fill: #F0F0F0
⚠️ Context lines need space prefix: size: 200 50 not size: 200 50
Apply with validation:
figma-use diff apply patch.diff # Fails if old values don't match figma-use diff apply patch.diff --dry-run # Preview figma-use diff apply patch.diff --force # Skip validation
Visual diff (red = changed pixels):
figma-use diff visual --from <id1> --to <id2> --output diff.png
⚠️ After initial render, use diffs or direct commands. Don't re-render full JSX trees.
Query (XPath)
Find nodes using XPath selectors:
figma-use query "//FRAME" # All frames figma-use query "//FRAME[@width < 300]" # Frames narrower than 300px figma-use query "//COMPONENT[starts-with(@name, 'Button')]" # Name starts with figma-use query "//FRAME[contains(@name, 'Card')]" # Name contains figma-use query "//SECTION/FRAME" # Direct children figma-use query "//SECTION//TEXT" # All descendants figma-use query "//*[@cornerRadius > 0]" # Any node with radius figma-use query "//FRAME[@width > 100 and @width < 500]" # Range
Attributes: name, width, height, x, y, `cornerRadi
Pros
- Streamlines design tasks through CLI.
- Supports both imperative and declarative modes.
- Integrates with JSX for component rendering.
Cons
- Requires Figma to run with remote debugging.
- Learning curve for non-technical users.
- Limited to Figma's capabilities.
Related Skills
flutter-claude-code
A“Powerful, but the setup might scare off the impatient.”
design-tokens-skill
A“Powerful, but the setup might scare off the impatient.”
claude-designer-skill
A“Powerful, but the setup might scare off the impatient.”
Disclaimer: This content is sourced from GitHub open source projects for display and rating purposes only.
Copyright belongs to the original author dannote.
