Skip to main content
Question

Preserving mixed text styles when replacing text via plugin API

  • June 23, 2026
  • 8 replies
  • 79 views

Ludivine Kasteleyn

Hi everyone, we translate Figma designs using AI. We've tried both a home-made Dust plugin and AI agents (Claude / Cursor) that write back to Figma via the plugin API and MCP. Both hit the same limitation: when a text node has mixed styles (different weights, sizes or colours within the same node), replacing the text flattens everything to a single style.

Is there a reliable way to preserve or restore mixed styles after replacing text content?

Would love to hear if others have found a workaround, or if this is just a current API constraint.

8 replies

Jaycee Lewis
Figmate

Hey ​@Ludivine Kasteleyn 👋 Happy Config week! Thank you for the great question. I’d love to dig in more and work on this together. I have a question for you: Are the mixed styles linked Text Styles, or raw character-range overrides?

Talk soon! — Jaycee


Ludivine Kasteleyn

Hey ​@Jaycee Lewis thanks for your reply and happy Config week! 

I’d say it’s related to raw character-range overrides. Whatever the setup (Claude/Cursor skill or Dust plugin), it never keeps bold or italic or bullet points for example.

Hope that helps you understand my issue :) 

Thanks again! Ludivine


Jaycee Lewis
Figmate

Hey ​@Ludivine Kasteleyn I hope you caught the livestream of the Config keynote. It was awesome 🤩 Thanks for the reply. That helps for sure. I have another question — When the translated text comes back, are you overwriting the whole node's text in one shot, or editing it segment by segment?

Thanks again! — Jaycee


Ludivine Kasteleyn

Hey ​@Jaycee Lewis not yet, will do shortly! But I already got a glimpse of it 👀

It overwrites in one shot yes. See part of the prompt in my screenshot. How could we make sure AI understand segments? I have two doubts:
- How can AI not embark the style from the first character only? (understand the notion of segments concretely without having to have each text style in a separate text layer)
- It’s not necessarily the same number of characters in French vs English or German for example. What if we have one word in bold in French and it should be two in German?

Thanks again for your help! Ludivine

 


Jaycee Lewis
Figmate

Hey ​@Ludivine Kasteleyn 👋 Thanks again for sharing the snippet. 

Thanks for the screenshot, that's helpful. Let’s talk about why the styling disappears.

Looking at the docs, this is expected behavior rather than a bug, and your step 4 is the exact spot. Setting textNode.characters = "..." is documented to reset all styles applied to character ranges — so the moment that line runs, the bold/italic/color/bullet info is gone and everything collapses to a single style. That's also why it "embarks the style from the first character only": the flattened node takes the leading run's style and applies it to everything.

Based on my read of the dev docs here, it's the setter itself doing the flatten — not the AI misunderstanding the instruction.

Here's a quick console repro if you want to see it directly (Plugins → Development → Show/Hide console). Make one text node with mixed styling — bold/italic/colour on different words — select it, then run:

const node = figma.currentPage.selection[0];
const fields = ['fontName','fontSize','fills','indentation','textDecoration'];
const before = node.getStyledTextSegments(fields);
const fonts = node.getRangeAllFontNames(0, node.characters.length);
await Promise.all(fonts.map(f => figma.loadFontAsync(f)));
node.characters = "Rapport bilan comptable 2025";
console.log("before:", before.length, "| after:", node.getStyledTextSegments(fields).length);

On my test: before: 5 | after: 1 — five styled segments collapse to one, the color drops entirely, and the whole string inherits the first word's bold.

Before

After

Let me know what you think! Talk soon — Jaycee 


Jaycee Lewis
Figmate

One thought on preserving the styling 🤔

Have you considered overwriting characters directly? To capture the style map first and reapply it after?

  • Read the styles with getStyledTextSegments before replacing — pass the properties you care about, e.g. ['fontName', 'fontSize', 'fills', 'listOptions', 'indentation'].
  • Load every font used (loadFontAsync), then write the translated text.
  • Reapply each segment's styles over the new ranges with the matching setRange… setters (setRangeFontName, setRangeFills, setRangeListOptions, etc.).

The Working with Text guide walks through this whole flow — loading fonts and handling mixed styles — and is the best single reference for the pattern.

For your bullets specifically: those are listOptions / indentation, not font properties — so if they're not in your getStyledTextSegments call, they won't come back even when bold and color do. Could be why the bullets drop.

This is worth consideration for preservation when the translated text is the same length and word order. — Jaycee


Jaycee Lewis
Figmate

Last thought on doubt 2 — different FR/DE lengths 😊

Reapplying styles by character offset gets weird the moment the translation isn't the same length or word order, because the old start/end indices no longer line up. I had Claude help me with a more robust test of this. Happy to share the script. 

Two findings: extra characters cause drift (only the tail-anchored segment survives), and reordering breaks it entirely (as you noted, your FR > DE case has no reliable offset to map onto 😢)

Sooooo, getStyledTextSegments + setRange… preserve that styling existed, but offset remapping can't preserve which words it belonged to once length changes. 

Have fun building! — Jaycee

 


Ludivine Kasteleyn

Hey Jayce 👋

My apologies, I took a bit of time to answer. We tested your solution and it worked!

 

Many thanks for your help!