# Creating Component Variants Programmatically in Figma: A Complete Guide

> Learn to create Figma component variants programmatically using the createComponentWithVariants helper. Automate Cartesian product combinations and build ComponentSetNodes efficiently.

- Repository: [Figma/mcp-server-guide](https://github.com/figma/mcp-server-guide)
- Tags: how-to-guide
- Published: 2026-03-30

---

**Use the `createComponentWithVariants` helper in [`skills/figma-generate-library/scripts/createComponentWithVariants.js`](https://github.com/figma/mcp-server-guide/blob/main/skills/figma-generate-library/scripts/createComponentWithVariants.js) to automatically generate Cartesian product combinations of variant axes, instantiate individual `ComponentNode` objects for each combination, and combine them into a `ComponentSetNode` using `figma.combineAsVariants`.**

The `figma/mcp-server-guide` repository provides a production-ready automation script that eliminates manual component duplication through the Figma Plugin API. This approach ensures consistent naming conventions and proper variant structure while following design-system best practices that mandate establishing variant axes before adding non-variant properties.

## The Eight-Step Variant Generation Workflow

The `createComponentWithVariants` function orchestrates the entire component lifecycle through a deterministic eight-step process. Each step targets specific Figma Plugin API methods to ensure thread-safe node creation and proper canvas organization.

### Step 1: Page Preparation

The function begins by guaranteeing the target page is active before any node creation occurs. It calls `await figma.setCurrentPageAsync(page)` to prevent race conditions that could scatter components across multiple pages. This awaitable API call is critical for maintaining deterministic behavior in async plugin environments.

### Step 2: Cartesian Product Calculation

The helper computes every possible combination of variant values using a local `cartesianProduct()` utility (lines 44‑48). This pure-JavaScript implementation leverages `Array.reduce` paired with `flatMap` to efficiently generate the matrix of property combinations without external dependencies.

### Step 3: Component Instantiation

For each combination in the Cartesian product, the script calls `figma.createComponent()` and formats the node name according to Figma’s variant naming convention (`"Property=Value, Property2=Value2"`). This occurs at line 53 in the source file. The function applies all **base visual properties**—including `width`, `height`, `fills`, `cornerRadius`, and auto-layout settings—before the combination step, ensuring each variant inherits the correct geometry.

### Step 4: Metadata Tagging

To enable later discovery and cleanup operations, each component receives persistent metadata through `comp.setPluginData()` (lines 88‑94). The script stores a deterministic `dsb_key` and an optional `runId` parameter, allowing external scripts to query and manage generated nodes reliably using `getPluginData()`.

### Step 5: Variant Combination

The script transforms the flat array of components into a unified `ComponentSetNode` by invoking `figma.combineAsVariants(components, page)` at line 100. After this API call, Figma automatically recognizes the shared variant axes across all children, enabling the variant picker interface in the design panel.

### Step 6: Grid Layout

To prevent variants from stacking at coordinates `(0, 0)`, the function iterates over `componentSet.children` (lines 13‑18) and calculates grid positions based on the size of the **last variant axis**. The algorithm applies a configurable `GRID_GAP` constant to ensure readable spacing between matrix elements.

### Step 7: Parent Resizing

The component set container expands to encompass all children plus padding using `componentSet.resize()` (lines 24‑27). The script uses a `PADDING` constant of `40` pixels to prevent canvas clipping and ensure the variant set displays fully within the viewport.

### Step 8: Canvas Positioning

Finally, the function positions the completed set at `x = 480, y = 80` (lines 30‑31) to avoid overlapping with existing file content. This safe-zone placement strategy keeps programmatically generated components organized and distinct from manually created design elements.

## Critical Implementation Details

### The Variants-First Rule

According to the design-system documentation in [`skills/figma-use/references/working-with-design-systems/wwds-components--creating.md`](https://github.com/figma/mcp-server-guide/blob/main/skills/figma-use/references/working-with-design-systems/wwds-components--creating.md), you must define all variant axes **before** adding non-variant properties such as text overrides, boolean operations, or instance-swaps. Adding these properties after variant creation risks destructive changes to existing component instances throughout your design files.

### Visual Property Timing

All geometry and styling attributes—including `comp.resize()`, `comp.fills`, `comp.layoutMode`, and `comp.padding*` values—must be applied **prior** to the `combineAsVariants` call. Properties set after combination do not propagate correctly to the individual variant nodes within the set.

### Line-Specific Architecture

- **Lines 44‑48**: Cartesian product generation using functional array methods
- **Line 53**: Component naming with Figma’s variant property syntax
- **Lines 88‑94**: Plugin data persistence for traceability
- **Line 100**: The atomic combination operation that creates the `ComponentSetNode`

## Code Example: Creating a Button Component Set

The following example generates a **Button** component set with two variant axes: `Size` (Small, Medium, Large) and `Style` (Primary, Ghost).

```javascript
// Generate a 3×2 matrix of button variants
await createComponentWithVariants(
  {
    name: "Button",
    variantAxes: {
      Size: ["Small", "Medium", "Large"],
      Style: ["Primary", "Ghost"]
    },
    baseProps: {
      width: 120,
      height: 40,
      radius: 8,
      fills: [{ type: "SOLID", color: { r: 0.0, g: 0.5, b: 1.0 } }],
      layoutMode: "NONE"
    },
    page: figma.root.pages[0]
  },
  "run-12345"
);

```

**What this execution accomplishes:**

1. Calculates six total combinations (3 × 2) of Size and Style values
2. Creates six individual `ComponentNode` objects with the specified blue fill and 8px corner radius
3. Assigns variant-compliant names like `"Size=Small, Style=Primary"`
4. Tags each node with the `runId` `"run-12345"` for later bulk operations
5. Combines nodes into a `ComponentSetNode` and arranges them in a 2-column grid
6. Positions the final set at coordinates `(480, 80)` with appropriate padding

## Source File Reference

| File Path | Purpose |
|-----------|---------|
| [`skills/figma-generate-library/scripts/createComponentWithVariants.js`](https://github.com/figma/mcp-server-guide/blob/main/skills/figma-generate-library/scripts/createComponentWithVariants.js) | Core implementation containing the eight-step workflow and `cartesianProduct` helper |
| [`skills/figma-use/references/working-with-design-systems/wwds-components--creating.md`](https://github.com/figma/mcp-server-guide/blob/main/skills/figma-use/references/working-with-design-systems/wwds-components--creating.md) | Design-system guidelines emphasizing the "variants first" architecture |
| [`skills/figma-use/references/component-patterns.md`](https://github.com/figma/mcp-server-guide/blob/main/skills/figma-use/references/component-patterns.md) | Figma component-set model documentation and naming conventions |
| [`skills/figma-use/references/gotchas.md`](https://github.com/figma/mcp-server-guide/blob/main/skills/figma-use/references/gotchas.md) | Common pitfalls including stacked variants and missing plugin data |

## Summary

- **Use `createComponentWithVariants`** from [`skills/figma-generate-library/scripts/createComponentWithVariants.js`](https://github.com/figma/mcp-server-guide/blob/main/skills/figma-generate-library/scripts/createComponentWithVariants.js) to automate variant generation through Cartesian product mathematics.
- **Always apply visual properties before combination** to ensure attributes persist in the final `ComponentSetNode`.
- **Tag components with `setPluginData`** using the built-in `dsb_key` and optional `runId` parameters for reliable future discovery and cleanup.
- **Follow the variants-first rule** by defining all axes before adding text, boolean, or instance-swap properties to prevent instance corruption.
- **Position variants in a grid** immediately after `figma.combineAsVariants` to maintain organized canvas layouts and prevent overlapping nodes.

## Frequently Asked Questions

### What is the `createComponentWithVariants` function?

`createComponentWithVariants` is a helper utility in the Figma MCP Server Guide that automates the creation of variant component sets. It accepts a configuration object defining `variantAxes`, `baseProps`, and a target `page`, then executes an eight-step workflow to generate, combine, and position the resulting `ComponentSetNode` on the Figma canvas.

### Why must visual properties be set before calling `combineAsVariants`?

Figma’s `combineAsVariants` API captures the current state of each `ComponentNode` at the moment of combination. Properties applied after this call—such as fills, strokes, or auto-layout settings—do not automatically propagate to the variants within the set. Setting `comp.fills`, `comp.resize()`, and geometry attributes before line 100 ensures every variant inherits the correct visual specification.

### How does the script determine grid layout for variants?

The grid algorithm defaults to using the size of the **last variant axis** as the column count. It iterates through `componentSet.children` (lines 13‑18), calculating x and y coordinates based on index position modulo the column count, multiplied by a `GRID_GAP` constant. This creates a readable matrix layout rather than stacking all variants at the origin point.

### Can I use metadata tags to find these components later?

Yes. The script stores a deterministic `dsb_key` and your optional `runId` parameter using `comp.setPluginData()` (lines 88‑94). You can later query these values using `node.getPluginData("dsb_key")` or `node.getPluginData("runId")` to identify, select, or delete programmatically generated components without affecting manually created design elements.