An icon set lives in Figma, and the product uses it in React. Between the two there is usually a person: someone exports SVGs, renames files, pastes them into the repository and hopes nothing else changed. This guide follows Figma icons to React components along the whole path: from an icon component in Figma to a merged pull request, with what each step produces.
We build FISYCO, which automates this path. The steps below work with any tooling, and the examples are real output of our generator for three icons.
Key takeaways
- Treat icons as data: read them from Figma by node ID, not by file name, so a rename is a rename and not a new icon.
- Pick one delivery format per product: React components, one SVG sprite, or a package of SVG files. Each has a clear best case.
- Clean every SVG twice: sanitise it when it enters your system, optimise it when you generate code.
- Deliver changes as a pull request with a readable diff, and trigger it from a library publish in Figma.
Step 1. Decide what counts as an icon
Figma files mix icons with everything else, so the first decision is scope. Point the export at the pages and sections where the icon components live, not at the whole file. A section per category, such as Navigation, Status or Social, keeps the scope readable, and the section name can become part of the output path later.
Read components, not loose frames. A component keeps its node ID when it's renamed or dragged within the file; its name is just text. Everything downstream depends on that ID: it's how you tell "renamed" from "deleted and added".
Step 2. Export SVG from Figma
Figma's REST API renders nodes as images through GET /v1/images/:file_key with format=svg (Figma REST API: images). The call takes a list of node IDs and returns a temporary URL for each SVG. Large sets go in batches: we request 100 nodes per call and cap each SVG at 1 MB.
Figma's plan comparison marks only the Variables REST API as Enterprise-only, so exporting icons doesn't hit that limit (why variables need a workaround).
Step 3. Clean the SVG on the way in
An SVG is a document, and a document from outside can carry things you don't want in your codebase. For example: <script>, event handlers such as onload, <foreignObject> or links with a javascript: URL. Remove them before the file is stored or previewed. We sanitise every SVG before it's written to our registry, and all generated code is built from that cleaned copy.
The same step is the right place for quality checks that don't change the file: a missing viewBox, empty paths and groups, colours that won't follow the text colour. Report them to the designer rather than fixing them silently.
Step 4. Know what changed
Compare the new export with the last one by node ID and content hash:
Moving an icon out of the selected pages or sections counts as removed. This table is what makes the pull request reviewable. Without it, every export looks like "all icons changed", reviewers approve on trust, and production quietly drifts from the design. With it, a reviewer sees three modified icons and one rename.
Names need one more rule. Normalise the Figma name to something code can use: lower case, spaces to hyphens, only a-z0-9-. When two icons normalise to the same name, flag the conflict and suggest name-2, instead of letting one file overwrite the other.
Step 5. Pick a delivery format
There are three sensible ways to ship icons to a React codebase.
React components
This is the output for an arrow icon, generated with TypeScript, named exports, a 1em size and one colour replacement, #1A1A1A → currentColor:
import * as React from "react";
import type { SVGProps } from "react";
const ArrowRight = (props: SVGProps<SVGSVGElement>) => <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 24 24" {...props}><path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 12h14m-6-6 6 6-6 6" /></svg>;
export { ArrowRight };Three details matter. 1em makes the icon follow the font size around it. currentColor makes it follow the text colour, so hover and disabled states come for free. And spreading props onto the <svg> lets a consumer pass aria-label, className or a different size.
The colour replacement is the step people skip. Designers draw icons in a real colour, and without the replacement every icon ships with that colour baked in. It suits single-colour icons; multi-colour icons keep their fills.
For accessibility, pass aria-hidden="true" when an icon sits next to a text label, and role="img" with an aria-label when the icon carries meaning on its own (MDN: img role).
SVG sprite
A sprite is one hidden SVG with a <symbol> per icon:
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<symbol id="fsc-arrow-right" viewBox="0 0 24 24">…</symbol>
<symbol id="fsc-check-circle" viewBox="0 0 24 24">…</symbol>
</svg>A page includes it once and references icons with <svg><use href="#fsc-arrow-right" /></svg>. The prefix, fsc- here, keeps symbol IDs from colliding with other IDs on the page.
Package
A package keeps the plain files and adds a manifest, so any consumer can read the set:
src/icons/
├── icons/
│ ├── arrow-right.svg
│ ├── check-circle.svg
│ └── github.svg
├── index.json
└── package.jsonThe manifest maps each icon's ID and name to its file. That ID is the Figma node ID behind the icon, so a consumer can track it across renames too.
Step 6. Optimise when you generate
Optimisation belongs to generation, not to storage. Run SVGO with its default preset when you build components. Prefix IDs inside each icon too, so two icons with a <clipPath id="a"> don't break each other on one page. Keep the stored SVG unoptimised: if the optimiser changes, you regenerate from the original instead of optimising twice.
Step 7. Deliver as a pull request
Write the generated files on a branch and open a pull request against the repository. A good icons PR:
- touches only the files this delivery owns, so it can also delete icons that were removed in Figma without touching anything else;
- lists added, modified, renamed and removed icons in the description;
- carries a label for added, modified or removed icons, so release notes can pick it up.
Then let the normal review happen. A reviewer checks the diff of three icons, not a regenerated folder of two hundred.
Step 8. Trigger it from Figma
The last manual step is starting the export. Figma sends a LIBRARY_PUBLISH webhook when a library is published (Figma webhooks), and the event lists the components that were created, modified and deleted (created_components, modified_components, deleted_components). Use it to start a sync, and use the list to download only the changed icons instead of the whole set.
With that in place, a publish in Figma opens a pull request without anyone exporting files by hand.
Build it or run it
You can build this pipeline yourself: the Figma API, SVGO and a script that writes a branch cover most of it. The parts that take longest to get right are the unglamorous ones: stable IDs, rename detection, deleting only what you own, and a diff a reviewer can trust.
If you'd rather not maintain that, FISYCO runs the same steps. It scopes by pages and sections, sanitises every SVG and delivers components, a sprite or a package. After each library publish it opens a pull request. The same approach works for design tokens, see design tokens from Figma to code.



