Function for setting text and loading fonts

Hi all –

Setting the text of a text node (using the Figma plugin API) is not a straightforward thing.

  1. The font or style of any character in the string you plan to overwrite cannot be “missing.”
  2. Every font/style that exists in the current string must be loaded before overwrite
  3. Font loading is promise-based

The code below loads the necessary fonts before setting a text node’s text and tests for missing fonts. It works, and it’s surprisingly fast given the tedium it puts itself through.

Disclaimer: I’m a designer, so I’m certain this code can be improved. I enthusiastically welcome feedback.

// this function loads fonts and sets text. Throws error if a font is missing.
const setTextOfNode = (textNode: TextNode, text: string) => {
    if (textNode.hasMissingFont) {
        throw  "<missing font error goes here>";
    }
    const foundFonts = [] as FontName[];
    const len = textNode.characters.length;
    // check textNode's font & style character by character. It may contain multiple fonts/styles
    for (let i = 0; i < len; i++) {
        const fontName = textNode.getRangeFontName(i, i + 1) as FontName;
        // only add a font to foundFonts if it hasn't been added yet
        if (foundFonts.find(f => f.family === fontName.family && f.style === fontName.style) === undefined) {
            foundFonts.push(fontName);
        }
    }
    // get an array of loadFontAsync() promises, one for each entry in foundFonts[]
    const fontPromises = foundFonts.map(f => figma.loadFontAsync(f));
    // text is set only after all fonts are loaded
    Promise.all(fontPromises).then(() => {
        textNode.characters = text;
    });
    return textNode;
}

// sample usage
try {
    setTextOfNode(myTextNode, "Text to set");
  } catch (e) {
    // error handling for missing font goes here
  }

4 Likes

Hi!

Font loading certainly isn’t a straightforward thing, just as you said. The docs could be much better IMO on this topic.

I got the fonts working like this:

// Function for loading all the needed fonts
const loadFonts = async () => {

  await figma.loadFontAsync({ family: "Work Sans", style: "Regular" })
  await figma.loadFontAsync({ family: "Work Sans", style: "Medium" })
  await figma.loadFontAsync({ family: "Work Sans", style: "Bold" })

  console.log("Awaiting the fonts.")

}

// Load the fonts by running the function
loadFonts().then(() => {

  // Make them useful
  doSomethingElseWithThem()
  console.log("Fonts fetched and ready to go!")

})

Maybe this helps someone!

2 Likes

Hi! I like this solution. So if you know all fonts that will exist in the file, I assume you can just say…

loadFonts.then(() => main())

… assuming main() is your main routine.

My issue is that I don’t know the fonts ahead of time, but it bugs me that for each text node’s string I set, I’m likely loading the same fonts I loaded upon setting the previous text node. It’s fast, so I’m assuming Figma is doing some kind of optimization in the background, but this is one of the rare cases where Figma’s documentation is lacking, at least in best-practice advice.

I come from Sketch though, where the Plugin documentation is simply a fireable offense! Commenters on that forum are forced to interpret Sketch’s header file just to figure out how all of the undocumented & under-documented functions work. Sketch changes its API without warning, and then your plugin breaks because they changed the way a function works, or dropped the function altogether.

In short, after years of writing plugins for Sketch, I can hardly complain about Figma’s docs! :slight_smile:

Yikes! The Sketch DX sounds awful :sweat_smile:

Good luck with your issue! If I ever come across a solution to your exact problem, I’ll come back here to tell about it :+1: