January 1, 2023

...

6 min read

How Figma API can be useful for design system

Intro

Figma is a popular web-based design tool used by designers and developers to create, share and collaborate on designs. Figma provides an API that allows developers to integrate Figma with their own applications. In this article, I will show how the Figma REST API can be useful for design system in React.

Design system, Figma and React

Design systems have become a popular way of building and maintaining consistent user interfaces across different applications. A design system provides a set of reusable components, guidelines, and assets that can be used to create consistent UI across different platforms. React is a popular front-end library that allows developers to build reusable UI components. By combining Figma and React, we can create a powerful tool for designing and building UI components.

Figma REST API

The Figma REST API allows developers to access and manipulate Figma files programmatically. With the Figma REST API, we can extract information about a design file, such as the layers, styles, and components used in the design.

One of the key benefits of using the Figma REST API for design systems in React is the ability to synchronize design changes automatically. When a designer makes a change to a Figma file, the changes can be automatically reflected in the React design system. This means that developers can easily keep their design system up-to-date with the latest changes in the design.

Another benefit of using the Figma REST API for design systems in React is the ability to easily import design assets into React. The Figma REST API allows developers to extract images, icons, and other design assets from a Figma file and use them directly in React. This makes it easy to use the same design assets across different applications and ensures consistency in the design. In addition to these benefits, the Figma REST API also allows developers to automate repetitive design tasks, such as generating design specs, creating design prototypes, and exporting design assets. By automating these tasks, developers can save time and focus on more important tasks.

In this section, we will look at example of how the Figma REST API can be used to support DS.

Extracting design assets

Let's say we have a Figma file that contains a set of icons, such as the ones shown below. We can use the Figma REST API to extract these icons.

figma-frameFigma frame with icons

First of all, we need to create a Figma personal access token. This token will be used to authenticate our requests to the Figma REST API.

I will show example with zx, ora, dotenv and dedent packages.

You can also use figma-api package, which is a wrapper around the Figma REST API, to simplify the working process with the API.

import dotenv from "dotenv-safe";
import dedent from "dedent";

// let's setup environment variables
try {
  dotenv.config({
    example: path.resolve(__dirname, ".env.example"),
    path: path.resolve(__dirname, ".env"),
  });
} catch (err) {
  if (err.missing.includes("FIGMA_TOKEN")) {
    throw new Error(
      dedent`
        Figma token is missing in the .env file:
        ${err.missing.join("\n")}
        You can generate one in your Figma account settings
      `,
    );
  }
}

then let's create a function to call the API:

import dotenv from "dotenv-safe";
import dedent from "dedent";

import { fetch } from "zx";

...
async function api<T>(url: string): Promise<T> {
  return fetch(url, {
    headers: {
      "Content-Type": "application/json",
      "X-Figma-Token": process.env.FIGMA_TOKEN,
    },
  }).then(res => {
      if (res.status !== 200) {
        throw new Error(`Request failed with status code ${res.status}`);
      }

      return res.json() as Promise<T>;
    });
  })
};

let's see what request will return to us:

const FIGMA_API_FILE_COMPONENTS = `https://api.figma.com/v1/files/${process.env.FIGMA_FILE_ID}/components`;
const FIGMA_IMAGE_URI = `https://api.figma.com/v1/images/${process.env.FIGMA_FILE_ID}`;
const FILTERED_ICONS = ["Social", "Navigation icons", "Icons"];

(async () => {
  const filesSpinner = ora(chalk.bold.underline.magenta("Fetching file ids...")).start();
  const icons = await api<Response>(FIGMA_API_FILE_COMPONENTS);
  if (icons.meta && icons.meta.components) {
    // We might need to exclude some of the components
    // Designers might have different frames with icons: regular icons, social icons,
    // navigation icons, feature icons and so on.
    const iconIds = icons.meta.components.filter(icon => !FILTERED_ICONS.includes(icon.name));
    console.log(iconIds[0]);
  }
})();

If you are writing the script in esm js with zx, you can simply call it like zx filename.mjs command, otherwise you can use ts-node to run the script, for example: ts-node file.ts or ts-node --esm file.mts

that should give us an array of objects like this:

{
  "key": "278ca56f16779dd01fc7dcdbc7af73ed0bc80225",
  "file_key": "wjYAT0sNBXtirEoyKgXUwr",
  "node_id": "2991:1025",
  "thumbnail_url": "<some url>",
  "name": "size=small",
  "description": "",
  "description_rt": "",
  "created_at": "2023-02-07T17:23:16.252Z",
  "updated_at": "2023-02-07T17:23:16.252Z",
  "containing_frame": {
    "name": "Social",
    "nodeId": "2991:1027",
    "pageId": "0:1",
    "pageName": "Icons",
    "backgroundColor": "rgba(0, 0, 0, 0)",
    "containingStateGroup": { "name": "Facebook", "nodeId": "2991:1020" }
  },
  "user": {
    "id": "799215572651301695",
    "handle": "Chuck Norris",
    "img_url": "<img url>"
  }
}

from this object we need only node_id, it's a unique identifier of the component, we can use it to get the svg

Depending on the number of icons you have in the file, you might need to split the request into chunks, because get request has a limit of characters in the query params. But for the sake of simplicity, I will show you the simple way.

(async () => {
  const filesSpinner = ora(chalk.bold.underline.magenta("Fetching file ids...")).start();
  const icons = await api<Response>(FIGMA_API_FILE_COMPONENTS);
  fileSpinner.succeed("File ids fetched");

  if (icons.meta && icons.meta.components) {
    // We might need to exclude some of the components
    // Designers might have different frames with icons: regular icons, social icons,
    // navigation icons, feature icons and so on.
    const icons = icons.meta.components
      .filter(icon => FILTERED_ICONS.includes(icon.name))
      .reduce((acc, icon) => {
        // or icon.name, depending on how your icons are organized
        acc[icon.id] = icon.containing_frame.name;
        return acc;
      }, {});

    const params = new URLSearchParams([
      ["ids", decodeURIComponent(Object.keys(icons).join(","))],
      ["format", "svg"],
    ]);

    const data = await api<Icon[]>(`${FIGMA_IMAGE_URI}?${params}`);

    if (data.images) {
      for (const icon of data.images) {
        const [id, url] = Object.entries(icon);
        const iconName = icons[id];
        const svgContent = await fetch(url).then(res => res.text());
        await fs.writeFile(path.join(__dirname, "icons", `${iconName}.svg`), svgContent, "utf8");))
      }
    }
  }
})();

That's it, now you can use svg files in your project. This also can be improved, if you will add a cron job, that will check for updates in the file and update the icons, so you will not need to do it manually every time