Rethinking Design Systems: When Code Becomes the Source of Truth
- Design Systems
- Figma
- Engineering
- AI
The Design-to-Development Gap
Every product team knows this dance: Design creates beautiful components in Figma. Engineering builds them in React. Then something breaks. The button in production doesn’t quite match the button in Figma. Someone updated the design. Someone else updated the code. Nobody updated both. The documentation is definitely not up to date either.
We tell ourselves this is inevitable. Design tools and code are fundamentally different beasts. The best we can hope for is “close enough” and good documentation.
I think we’ve been solving this backwards.
This is the first in a series about building design systems that actually work. Figma doesn’t have to be just pretty pictures for engineers to interpret. It can be a structured system that directly informs code generation. Design and engineering don’t have to be two sources of truth struggling to stay in sync. They can be a single system with design tools as one interface and code as another.
Sound impossible? I’ve built a proof of concept, and I think there’s something here.
The Vision: Reverse the Flow
The typical process looks like this:
- Design in Figma: Create component mockups with variants, states, interactions
- Spec it out: Write documentation explaining what engineers should build
- Implement in code: Engineers interpret the designs and build React components
- Publish to Figma: Designers use these components in mockups
- Drift begins: Design updates Figma. Code updates independently. Documentation lags behind both.
The problem? We’re maintaining two separate sources of truth and hoping they stay synchronized through diligence and documentation.
What if we flipped this? Here’s the component library workflow:
- Design conceptually in Figma (sketches, explorations, mockups)
- Implement the component in React based on those designs
- Build testing, Storybook, documentation (the real specification)
- Generate Figma components from the code ← This is the flip
- Publish these code-generated components to the shared Figma library

The Figma component library stops being aspirational and starts reflecting what actually exists in production. When a designer uses a button component in Figma, they’re using the button component in code, just rendered in a design tool.
These code-generated Figma components carry metadata. Design handoff tools and AI agents don’t have to guess that a button maps to <Button variant="primary" size="large">. They can know this because the component itself contains that information.
What This Requires
This approach demands rethinking some assumptions about component libraries. Design tools become where component concepts get explored and refined, but the component library itself reflects the code. Product designs still happen in Figma using these components. But the components designers use are no longer aspirational—they’re direct representations of what exists in production. Documentation stops being a bridge between two systems and gets embedded in the system itself.
Early-stage products iterating rapidly might not want this level of structure yet. But small teams building for the long term could benefit significantly—this is exactly the kind of foundation that prevents the tech and design debt that accumulates as teams grow.
But if you’re building a mature product with a design system, constantly fighting drift between Figma and code, and dreaming of better design-to-development handoffs? This might be interesting.
The approach requires solving some hard technical problems: generating Figma components programmatically, tracking changes efficiently, maintaining sync between systems, and handling edge cases gracefully.
I’m starting with something concrete: icons.
If this is the easiest part of the vision (and honestly, it is), then succeeding here proves the approach is feasible. Failing here means the bigger vision isn’t practical.
The plugin and automation are open source: github.com/joshjhall/google-symbols-figma-plugin
Why Icons Make Sense as Step One
Icons might seem like a small part of the bigger vision, but they’re the perfect place to start for three practical reasons.
I need them anyway. Whether or not the code-driven design system idea works, I need a comprehensive, up-to-date icon library in Figma. If the larger experiment fails or becomes too complicated, this part still delivers value to both design and engineering today.
It’s a learning ground. Building a Figma plugin that pulls data directly from a Git repository and generates components programmatically? That’s exactly what I’ll need to do when generating components from React code later. Better to learn on a well-structured external repository than on my own codebase.
It forces optimization. With nearly 4,000 icons and 504 variants each, that’s close to 2 million total variants to manage. GitHub rate limiting. Figma memory constraints. Partial import failures. Incremental updates. All the problems I’ll face when processing a real codebase at scale need solutions here first.
It took several days to get all the icons initially imported into Figma. The process would fail partway through due to HTTP rate limits when downloading raw files directly from GitHub (turns out downloading 75,000+ SVG files is enough to hit those limits). I’d restart it. It would fail again, differently. I spent a lot of time staring at progress bars and error logs.
Those same strategies (graceful recovery, smart batching, delta-only updates) will be essential when re-processing codebases for changes. If rebuilding from scratch takes hours or days, the system won’t work in practice.
Why Google Material Symbols?
I needed an icon system that could work as a shared foundation. Google’s Material Symbols fit perfectly.
The library is comprehensive, with nearly 4,000 icons covering most interface needs. But more importantly, it’s a proper system, not just a collection of SVGs. Each icon has 3 visual styles (Outlined, Rounded, Sharp), 7 weights (100-700, like typography), 2 fill states (empty or filled), 3 optical grades (adjusts for light/dark backgrounds), and 4 standard sizes (20, 24, 40, 48dp).
That’s 504 explicit variants per icon (3 × 7 × 2 × 3 × 4). Google ships these as variable fonts for production use, about 1.5MB per style or 4.5MB total, highly optimized for web delivery. But they also maintain the source SVGs these fonts are built from, which is what the plugin uses.
Why SVGs for Figma? Because Figma needs actual vector paths to render icons correctly. Using SVGs means the Figma components look exactly like production, with all the same configuration options clearly represented for engineering and other stakeholders. Then the actual implementation can use the optimized font files rather than embedding SVGs directly.
The raw SVG source is nearly 1GB. Two million very small files add up quickly.
Universal delivery: The library is already cached on millions of devices. Developers know it. Designers recognize it.
Active maintenance: Google updates it regularly with new icons and refinements. If I’m building a system around staying synchronized, I need a source that actually changes. That said, there have only been about 10 updates in the last 18 months, so changes happen infrequently.
![]()
If both my Figma components and my React components use the same Material Symbols system with the same naming and variants, we have a shared vocabulary. A search icon in Figma maps to a search icon in code, with weight, style, and fill properties that mean the same thing in both places.
The Technical Challenge: Updates and Metadata
Building a static snapshot of 4,000 icons is straightforward. Building a system that stays synchronized as Google updates their repository was the hard part. Here’s how I solved the three trickiest problems.
Tracking Changes with Metadata
Each icon component in Figma stores metadata using Figma’s setPluginData() API. Specifically, each icon component stores the Git SHA from the commit that last changed it in Google’s repository, and each variant frame stores a hash of its SVG content.
When Google updates the repository, my automation script (running weekly via CI) analyzes which icons changed between the old commit and the new one. It notifies me and calculates the delta information needed for the plugin.
Then I manually run the plugin on each of the 26 files. Running all 26 takes about an hour or two total now. The initial build took nearly an hour or more per file, over 26 hours total. The plugin only regenerates icons that actually changed.
Take the pentagon icon. Google has a bug in the 20dp optical size alignment. (Yes, I check every update hoping they’ve fixed it.) When they eventually fix it, the script identifies that pentagon has a different Git SHA than what’s stored in Figma. The plugin downloads all 504 SVG variants, computes content hashes for each, and compares them with stored hashes. Only the 20dp variants have different hashes, so only those frames get updated. Icons without updates are skipped entirely.
What used to take 26+ hours becomes a 1-2 hour update cycle.
Handling Edge Cases
Deprecations: When Google deprecates an icon, I don’t delete it immediately. The plugin prefixes the component name with _deprecated_. Designers can still see deprecated icons in their existing designs while preventing new usage. On the next publish, these can be cleanly removed from the shared library.
New additions: When Google adds new icons, they need to fit into the existing alphabetical organization. The 26 files are split alphabetically, with most files containing about 160 icons ranging from 80 to 200. New icons are added to whichever adjacent file has fewer icons, and the filename range gets updated accordingly. Each file is 28-39MB, 900MB total. Keeping files balanced prevents unnecessary icon moves and stays within Figma’s memory constraints.
Graceful recovery: During initial development, partial failures were common. I’d start building 170 icons for a file, get 70% through, then start hitting rate limits. Instead of getting all 504 variants for an icon, I’d only get 274 SVGs. The rest returned 404 errors.
The plugin creates partial icons with whatever SVGs it successfully retrieved. Then I’d wait a few minutes and rerun the plugin on the same file to backfill the missing variants. Some files I had to rerun several times. Often because I was getting impatient and didn’t wait long enough between attempts.
On the plus side, most of this ran in the background while I worked on other things. On the downside, it still took a bit of my attention off and on for several days to complete the initial import.
But this robustness is exactly what I mean by “scalable to more complex scenarios.” Icons are simple. Just SVG paths. When I eventually generate components from React code, I’ll likely need to download JSON schemas, API responses, or other data to describe what’s needed for complex components. I’ll hit those same errors. The system needs to handle partial failures gracefully and resume where it left off.
The Result: A Living Icon Library
With all these strategies in place (delta updates, smart batching, metadata tracking, and graceful recovery), the system delivers what I set out to build: a living icon library that stays current with minimal manual intervention.
The plugin generates 26 Figma files, organized alphabetically, with nearly 4,000 icons and their 504 variants. These files are exported and attached to GitHub releases, so anyone can download pre-built .fig files and use them immediately.
More importantly, the system stays current. A GitHub Action runs weekly, checks Google’s repository for changes, calculates the delta if changes are detected, runs tests on the changes, and notifies me with an optional mobile push notification.
When I merge the changes and run the plugin on the 26 files (an hour or two of work), only the changed icons regenerate. Given that updates happen roughly twice a year, we’re talking about just a few hours of annual maintenance. Additional automation either isn’t possible in Figma’s environment or wouldn’t save enough time to be worth building.
The plugin and pre-generated .fig files are available in the latest release: v1.2.2
The repository also takes advantage of my universal docker container system for consistent development and CI/CD environments. That system is open sourced at github.com/joshjhall/containers, with a detailed write-up about the approach: Building a Universal Container System.

How These Files Actually Get Used
I’m going to dive deeper into this in the next post, but the brief version: my design system has multiple layers, each serving different purposes.
1. Core components (where code-generated components will live): Fundamental UI elements like buttons, fields, sheets, and dialogs. The goal is to build these in React, then generate them back into Figma and publish to the library. Today these are manually maintained, but this is where the code-driven approach will eventually land.
In Figma, I use naming conventions to convey information to engineers. Components prefixed with an underscore (like _icon) are private and won’t be published directly to the shared library. Think of these like private classes. Components wrapped in angle brackets (like <icon>) indicate React component boundaries, helping engineers understand where component splits likely make sense.
The core file includes all 26 icon files as dependencies and has a base _icon component with a swappable child symbol. An <icon> component enhances this with sizing and optional badge decorations. The _icon component maintains a preferred icons list, filtering the full 4,000 icons down to 100-300 that the team actually needs. Common icons like close, menu, and search are universal, but there’s about 20-60% variation between different apps.
A small set of heavily-reduced icon variants (24 variants each instead of 504) are embedded directly in the core components file for things like checkboxes, radio buttons, and dropdown arrows. Keeping these embedded means the core file stays clean of dependencies except for the main _icon preferences.
2. Product-specific components (where partially code-generated components will live): Specialized components and configured variations of core components. The vision is for some to be generated from code, while others are designed in Figma then built in code.
3. Product designs (never code-generated): Actual screens, flows, and states designed using components from layers 1 and 2. This is where designers work day-to-day, combining existing components to design new features.
There’s also an implicit exploration layer that doesn’t fit neatly in this hierarchy: component concept files that consume core and product components to explore new component ideas. These inform the code development that eventually generates new components for layers 1 and 2.
Designers working on product features only need to include the core components and product components in their files. The icon filtering is already done at the system level. Adding new icons to the preferred list requires a deliberate decision and a new publish from core components. Just enough friction to make teams think about it, and in larger organizations, forces collaboration between teams before icon proliferation gets out of hand.
More on component structure patterns in the next post.
Taking Stock: What This Proves
Building this icon library has proven several things:
- Programmatic Figma component generation works at scale
- Git-based synchronization can keep design assets current with external sources
- Metadata and delta updates make large-scale regeneration practical
- The same approach that syncs with Google’s repository can sync with a React codebase
But this is just the foundation. These are raw Material Symbols, exactly as Google publishes them. They’re the shared vocabulary layer, the common foundation that both design and engineering build upon.
In the next post, I’ll show how to actually use these icons in well-structured Figma components. We’ll look at what makes a good base component using buttons as an example, and how consistent practices in Figma can convey rich information to engineers.
We’ll also run into some of Figma’s frustrating limitations, which is part of why generating components from code starts looking increasingly appealing.
I’m still refining this approach. The icon foundation is working well in production. The component generation from React code is next. The full workflow hasn’t been battle-tested at scale.
But I think there’s something here. A way to build design systems that aren’t two separate systems hoping to stay in sync, but one system with multiple interfaces.
More to come.
Next in this series: Building effective base components in Figma—what makes a good button, and why some of Figma’s limitations make code-driven generation increasingly appealing
The plugin and source code are available at github.com/joshjhall/google-symbols-figma-plugin. Have thoughts on this approach? Find me on Twitter/X or open an issue on GitHub.