Apply & Generate
How Apply Data and Generate Instance work: mechanics, scope, slots, auto-hide, templates, and practical scenarios.
Apply Data
Apply Data is the core Database operation. It takes data from a dataset, runs it through mapping rules, and inserts values into Figma layers.
How it works
- The plugin collects all mappings and builds a map: layer name → field list.
- Based on the selected scope (Selection / Page / Document), it finds target nodes.
- For each matched layer, it determines the dataset and data row based on the fill strategy.
- It evaluates the Key Expression and expands nested substitutions.
- It applies the value: text, image, directive, or boolean.
- If Auto-hide is enabled, untouched layers are hidden.
Scope
| Scope | What gets processed |
|---|---|
| Selection | Only selected objects and their child layers |
| Page | All matching layers on the current page |
| Document | All matching layers in the entire file |
Warning: the Document scope may take significant time on large files. Use Selection for fast iteration.
Tip: with Selection scope, it's enough to select the root containers (cards, table rows). The plugin finds all target layers inside.
Field application order
When multiple fields from different mappings target the same layer, the order is:
- Fields with group
cell/tableapply first; - Then fields with group
cell/*; - Then the rest, stably sorted by mapping ID.
What happens for each value type
| Value type | Target node | Action |
|---|---|---|
| Plain text | TEXT | Replace characters |
| Image URL | Any with fill | Download and apply as image fill |
@prop:Name=val | INSTANCE | Set component property |
@hide | Any | visible = false |
@node:visible=bool | Any | Control visibility |
true / false (boolean) | INSTANCE | Control instance visibility |
Auto-hide unused
When Auto-hide unused is enabled in the mapping settings, after applying data the plugin:
- Finds all layers within the current instance / row shell.
- Layers that were not targeted by any mapping field and received no data are hidden.
- Layers that are mapping targets (even if data is empty) remain visible.
Tip: use Auto-hide for cards with optional elements: "Sale" badges, "Out of stock" labels, rating icons. If there's no data — the layer hides automatically.
Slot Repeater
During data application, the plugin can automatically duplicate content inside slots. This works for components with repeater logic, where a single template multiplies based on the number of rows.
Generate Instance
Generate Instance creates multiple component instances from a single template, each filled with a unique data row.
When to use
- You need to quickly create 10, 50, or 100 cards with real data.
- Fill a table or list with real content.
- Test how a component looks with different data variations.
Preparation
- Create a component (or use an existing one) — this is your template.
- Create a dataset with data for each row.
- Create a mapping binding fields to the component's layers.
- Select an instance of the component in your design.
- Click Generate Instance at the bottom of the Database tree.
How generation works
Step 1: Plugin identifies the container and template
↓
Step 2: Removes previous generated copies
↓
Step 3: Preloads fonts from the template
↓
Step 4: Creates copies (one per data row)
↓
Step 5: Applies the mapping to each copy
↓
Step 6: Selects the generated nodes
Row count
By default, instances are generated for all rows in the dataset. If you need fewer, specify the count in the interface. The plugin will use min(specified count, dataset row count).
Two generation modes
Mode 1: Regular host (Frame / Group)
The template is the first matching child of the container (instance, frame, or component).
- Row 1 → the existing template (updated with data).
- Row 2 → clone of the updated template.
- Row 3+ → each row clones the previous inserted copy.
Container (Frame)
├── CardInstance ← template (row 1)
├── CardInstance ← clone of template (row 2)
├── CardInstance ← clone of row 2 (row 3)
└── CardInstance ← clone of row 3 (row 4)
Why clone from previous rather than the template? This ensures stable internal layer IDs for Figma: each subsequent clone correctly inherits sublayer IDs from the latest instance.
Mode 2: Slot
When the template is inside a component slot, behavior adapts:
- Row 1 → the existing instance in the slot (updated).
- Row 2 → clone from the template.
- Row 3+ → clone from the previous inserted row.
This lets you generate content directly inside slots while maintaining proper component structure.
Important: after slot generation, the plugin normalizes element order and cleans up "ghost" nodes — artifacts that can appear from repeated duplication.
Nested slots (tables)
For structures like WTable (INSTANCE) → rows (SLOT) → row (INSTANCE) → table (SLOT) → cell, the plugin uses row cloning (clone() + insertChild) with subsequent prop syncing and slot-children order normalization. This is the only path that is safe for Figma: creating rows via createInstance() and removing the slot's original master template previously caused the entire WTable instance to cascade-delete (see Figma API limits below).
After cloning, the plugin runs a heal pipeline: normalizes row order, refreshes node handles, and attempts to move the master-inherited "ghost" row out of the slot (via insertChild into the parent) or hide it (visible = false).
Figma API limitation: the master "ghost" row
When the row template is an instance that itself lives inside a nested INSTANCE (the classic WTable case), Figma does not allow plugins to delete the slot's master-inherited row. In the worst case, one extra visible row remains in the slot after generation — this is not a plugin bug, it's a Plugin API constraint (2026-Q2): the documented safe override properties for an instance sublayer are visible, characters, mainComponent only; forcing a delete of such a node resolves to the parent instance.
If the plugin could not move or hide that row, it shows a one-time notification — just delete it manually. All other generated rows already contain your data.
Performance
Writing TextNode.characters to a sublayer of a deeply nested instance (4–5 levels WTable → rows → row → cell → text) costs ~800–900 ms per cell on Figma's side — this is not the plugin, it's the internal file relayout. The plugin warms up all fonts in one batch and writes directly without extra retries, but we can't go below that floor: the Plugin API does not expose setBulkOverrides or a way to freeze layout.
If you need speed on large datasets (tens or hundreds of rows), use a flat FRAME/AUTO_LAYOUT as the row template instead of a nested instance table — on a flat structure a text write costs ~2 ms, not ~800 ms. The plugin will also show a notification if Apply took longer than 5 seconds, with the average time per row.
Bind component — template verification
If the mapping has Bind component set, before generation the plugin checks:
- Is the selected template an instance of this component?
- Does
mainComponent.keymatch the mapping'stargetComponentKey?
If not, generation is cancelled with a warning.
Cloning internals
The internal copy-creation algorithm:
node.clone()— primary method.- If clone() fails → refresh handle and retry.
- Fallback: get main component →
createInstance(). - After creation: property sync between instances (
syncInstancePropsFromReference).
Figma limitation: cloning complex instances with deep nested overrides can sometimes lose properties. In such cases, the plugin automatically recreates the instance via
createInstance()and syncs props.
Apply after generation
After all copies are created, the plugin automatically runs Apply for each row:
- Each copy receives its own row's data.
- Composition templates (
{{ }}) are expanded individually per row. - Other mappings (not involved in generation) are not applied automatically.
Practical scenarios
Scenario 1: Product catalog
Goal: fill 20 product cards with real data.
Dataset catalog.csv:
title,price,image,category,inStock
"iPhone 15 Pro","$999",https://cdn.example.com/iphone15.png,Phones,true
"MacBook Air M3","$1,099",https://cdn.example.com/macbook.png,Laptops,true
"AirPods Pro 2","$249",https://cdn.example.com/airpods.png,Audio,true
"iPad Air","$599",https://cdn.example.com/ipad.png,Tablets,false
"Apple Watch Ultra","$799",https://cdn.example.com/watch.png,Wearables,true
Mapping:
| Key Expression | Target Layer | Fill |
|---|---|---|
[title] | product-name | A-Z |
[price] | product-price | A-Z |
[image] | product-photo | A-Z |
[category] | product-category | A-Z |
[inStock] | stock-badge | A-Z |
Steps:
- Create a card component with layers
product-name,product-price,product-photo,product-category,stock-badge. - Place one instance in a container frame.
- Select the instance → Generate Instance.
- Result: 5 cards, each with unique data.
stock-badge(Instance) is hidden for iPad Air (inStock = false).
Scenario 2: Pricing table
Goal: create a table with pricing plans.
Dataset plans.json:
[
{
"name": "Starter",
"price": "$0",
"users": "1 user",
"storage": "1 GB",
"support": "@prop:Type=Email",
"badge": "@hide"
},
{
"name": "Pro",
"price": "$29/mo",
"users": "Up to 10",
"storage": "100 GB",
"support": "@prop:Type=Priority",
"badge": "@prop:Visible=true;Label=Popular"
},
{
"name": "Enterprise",
"price": "Custom",
"users": "Unlimited",
"storage": "Unlimited",
"support": "@prop:Type=Dedicated",
"badge": "@prop:Visible=true;Label=Custom"
}
]
Steps:
- Create a pricing row component with the appropriate layers.
- Select the instance → Generate Instance.
- Result: 3 rows. Starter badge is hidden, Pro has "Popular", Enterprise has "Custom". Support type is controlled via the
Typeprop.
Scenario 3: Design system prop showcase
Goal: display all button states.
Dataset button-states.json:
[
{ "button": "@prop:Size=Large;Variant=Primary;Disabled=false", "label": "Submit" },
{ "button": "@prop:Size=Large;Variant=Secondary;Disabled=false", "label": "Cancel" },
{ "button": "@prop:Size=Small;Variant=Primary;Disabled=true", "label": "Disabled" },
{ "button": "@prop:Size=Small;Variant=Ghost;Disabled=false", "label": "Learn more" }
]
Mapping:
| Key Expression | Target Layer | Fill |
|---|---|---|
[button] | Button | A-Z |
[label] | button-label | A-Z |
Generation creates 4 button instances, each with a unique combination of Size, Variant, and Disabled.
Tips and warnings
Layer naming: give layers unique, descriptive names.
text-1,text-2— bad.product-title,product-price— good. This simplifies mapping and reduces errors.
Test on a small selection: before applying to an entire page, test the mapping on one or two elements with scope
Selection.
Fonts: before generation, ensure all fonts used in the template are available. The plugin preloads fonts, but if a font is unavailable, text layers may not update.
Nested instances: when generating from a complex component with deeply nested overrides, enable Bind component to verify template integrity.
Performance: generating 100+ instances with images may take time. Figma limits image download speed. For large volumes, consider splitting the operation into several batches.
Flat template vs nested instance table: for large datasets (>50 rows) a FRAME/AUTO_LAYOUT row template is filled hundreds of times faster than a row inside a nested
WTable → rows → row. This is a Figma Plugin API limit, not a plugin issue: writing text on a sublayer of a deeply nested instance costs ~800 ms per cell. If you don't specifically need an instance-based table, use a flat list.
Related pages
- Data formats and settings: Data files & mappings
- General overview: Database overview