Over the years, there has been much made about the building and maintaining of component libraries (typically referred to as “design systems” by designers) — particularly on the design side, but not as much on the front-end side. Nonetheless, I have always preferred the practical and sincere. To that end, I’d like to provide a practical and sincere guide to building a component library in Figma. Before I jump in, a few principles I’d like to consider:
Avoid principles
There’s much talk about various principles or frameworks designers should employ to build their libraries. I would caution against these more academic pursuits. A library is a tool, not an execution of a white paper. Beneath every reasonable philosophy is sound reasoning, so lead with the reasoning every time. Philosophies go out of fashion; reasonable, common sense solutions do not.
Do what is needed — and only that
This is a tool — nothing more. This is not something that is intended to be beautiful, used in presentations, etc. It is highly functional, and generally won’t be seen in whole by anyone else but the designers maintaining it. Build it quickly, maintain it well, make it minimal and performant, and move on to the next thing you need to do.
Plugin agnosticism
Make your library plugin-agnostic. Designers generally prefer Figma out of the box, no plugins required. Unless there’s a very sincere, specific use case for a plugin, just use Figma’s native functionality.
Speedrun your components
Wikipedia describes speedrunning as such:
A speedrun is a playthrough of a video game, or section of a video game, with the goal of completing it as fast as possible. Speedruns often follow planned routes, which may incorporate sequence breaking, and might exploit glitches that allow sections to be skipped or completed more quickly than intended.
The following is an example of a speedrun, where a game that normally takes 12 hours to beat is, instead, beaten in 5 minutes by exploiting shortcuts in the game:
However, speedrunning can be applied towards many concepts. Consider it applied to illustration:
To draw a heart, most might draw the perimeter of a heart, then add a fill. If you wanted to speedrun this, however, one could just draw one single, triangular path, set a stroke width, then add round caps. This is faster, and more easily maintainable.
Likewise, when you build a component, build it with the least amount of actions and objects necessary. This reduces performance hits in your file, and makes it easier for other designers on your team to maintain it alongside you.
There will be heretics
If you don’t see consistent, intended usage of your library (e.g., designers mostly breaking your components, copying components from other parts of their own files, etc.) your intended usage is probably very unpleasant for designers. Rarely, there may be people who quietly redesign components from your library in their own files — in other cases, they may even go so far so as to remake the whole library. In these cases, before casting judgment on their intent, dig into their file and try to derive what changes they are proposing, and why. (If you would like to persuade them to use your tool, I would recommend knowing about their alternative tool, first.)
Do not seek to mirror the codebase’s library
The component library maintained in the codebase does not have the same use case as the one maintained in Figma. More specifically: many props and components exist in the codebase that are not visual; likewise, there are props many people set (i.e., on icon libraries) that will never occur in the codebase. There is no way — nor reason — to attempt to reproduce these in the designer’s library. Do not seek to. Just make the tool you need, not the one you think you need.
Design libraries accomplish a few things but, for developers, components are a great way to see all (or most) of the legal visual states a component can be in. However, many more things will be happening behind the scenes that the designer cannot reproduce in their own libraries.
Use props and name them well
Make variants you think will be useful. Do not include variants that you do not think will be useful to designers (e.g., most hover states, if they’re already in the codebase, likely do not need to be actively maintained in the design library). Make the prop names reasonable so that designers can predict what the prop will do before using one.
Prioritize making a library, not a sticker sheet
Don’t build a sticker sheet — instead, build easily searchable components with lots of props. Designers want to use the asset search to find components and drop components in to their own files. They do not want to keep a file open, scrub through it to find the component they’re looking for, then copypaste it in their file. Also, your components should be usable, above all: namely, have lots of props set to suit the designer’s needs, have lots of responsiveness built into them, etc.
Setting a foundation
You have a few options for how to set up your file structure:
- You could have one project with a bunch of files in it, ranging from feature work files to library files, like so:
- Or you could make projects that correspond to feature teams, and have those as siblings to your project for your component library(ies), like so:
- If you’re an enterprise customer, you can make a whole organization for your libraries. That’s fine, too.
This choice is easily reversible, so whatever you decide here is not critical. You can always move these files around in Figma. Regardless of what you choose, you can set your library(ies) to be linked by default to any new files by adjusting this setting in your organization:
Color and type styling
You probably want to have some colors and type styles saved to use throughout. Some people refer to these “design tokens.” You have some options:
- Have a file (I called it “Variables,” but it doesn’t matter) dedicated to your colors & type styles and use that as an include across your organization (for example, as a sibling file to your iconography file, and your components file), like so:
- Or, you can add them to the file where you’re saving your components. Personally, I prefer this. Here’s an example:
As for how to set up your colors, I prefer something like this:
In this example, you can see a few things:
- “Swatches,” (or “Main” or “Quick Picks,” doesn’t matter) or some place where you can see the main, highest saturation colors that most designers will use,
- Detailed color breakdowns (e.g., “Red,” “Blue,” “Grays”), where designers can get a range of colors in each palette — ranging from light to dark, including those main colors, too (e.g., “Red 500”),
For text styling, I would recommend something like this:
Additional thoughts
- Some people like to include text colors, like link colors, etc. But I feel this adds more to maintain. For example, if all your links are
Red 500
, I would just useRed 500
on those links, rather than add another color style calledText Color/Link
or something like this. Now you have three Red 500s to maintain, rather than two, - If you do want to add text colors, and you maintain dark and light modes, something I’ve seen people do that is useful is something like:
Text Colors/Link
(for text link colors on light mode) andText Colors/Link (Reverse)
(for text link colors in dark mode). I’ve used this before and it’s pretty convenient, as far as text color styles go, - Some people like to add visual presentations of their color palettes and type scales as documentation. I don’t like to add documentation into my libraries in general, for numerous reasons that I provide more detail on below (under
Components/Additional thoughts
). Unless there’s a specific use case for adding in visual color swatches/type scales, I would not do this, - Generally, do not add line heights to your type styles. Always assume the browser/target device will calculate the hitbox of the text in its own way — because it will. A use case for line heights could be body copy, for example. Also, when setting line heights in Figma, set them as percentages in Figma (e.g.,
150%
), rather than as static values. In the browser, these are not static values, but percentages (e.g.1.5
). This is useful because it’ll scale with changes in size and typeface, should this ever happen (in Xcode, line heights are set as multiples, AFAIK, but correct me if I’m wrong). For example:
- Don’t try to emulate the codebase, particularly with respect to things like line heights (this will always calculate differently on every target device) or naming (naming should be clear in the designer’s daily usage, whereas developers can see the
font-weight
and other attributes in the Inspect panel if they so choose; a naming scheme likesys/light/on-background
is not intuitive to most people, and this could just beLight Mode/Body (Reverse)
or something to this effect — it’s okay that you are optimizing for usability instead of a 1:1 relationship with the codebase in every respect), - You can also save effects, like dropshadows, or blurs, etc. I save shadows like:
Shadows/Toast (Web)
orShadows/Toast (mobile)
, for example. - Some people like to have a lot of “semantic” color styles saved (
Borders/Nav Border
,Text Colors/Blue
,Text Colors/Blue Secondary
), which is fine, but it can get to be too much. Specifically: on top of your scale of colors (Purple/100, Purple/200, Purple/300, …
), you now have a bunch of colors that are duplicates of colors in that scale, and they are semantically named. This creates a lot of duplication of the same colors — if that color ever changes, now you have to make sure to change it in two places. This wouldn’t be the hardest thing in the world, but the biggest problem I see with semantic styles is that they often have exceptions — maybe you normally want to useBorder/Gray
as your border color, but you might have a perfectly good reason to not use that specific gray color in this border. For this reason, I’d recommend as few semantic styles as needed, and allow designers to use your scales to decide what colors it is that they need.
Iconography
You probably want to maintain a custom icon library.
If you don’t yet have iconography
If you’d like guidance on how to build your icons:
- Pick a size you want to design your icons at. It can be any. Lots of people like
24x24
, or32x32
. It doesn’t matter what the exact number is, just make sure you pick one, - Always design the icons at this agreed upon size. This keeps them visually consistent, no matter the size or location in which they are deployed. So, for example, don’t do this:
Instead, do this:
- Flatten all geometries (especially strokes) into one path. This has numerous benefits, including easier scaling of these icons. You can flatten geometries in both Illustrator or Figma (“Flatten Selection” and “Outline Stroke” in Figma).
If you already have iconography
You have two options for where to put your icons:
- Make a file inside of
Core
,Design Systems
,Component Libraries
(whatever you called it), just for icons, like so:
- Or, you can add a page inside the one file that contains both iconography and components. Personally, I prefer this for numerous reasons: there isn’t really a performance concern with icons, as they are very small and simple objects, and it reduces the amount of files one has so everything’s all in one place.
Setting your baseline
Regardless of your decision, your icons exist, and they have a place to live somewhere in Figma. Now, do these things:
- Pick one color for them all to default to. A lot of people like black. It can be anything you want, just pick one. This keeps things tidy, and also doesn’t cause problems with overrides as designers switch between icons,
- Remove any extraneous fills on both parent and child(ren),
- You probably want your icons to scale, so select the actual geometries and set them to “scale” in both X and Y directions, like so:
- Set the parent (and child(ren)) to be “locked,” so that designers can easily press up/down in the W/H inputs and the icon will scale as expected, like so:
- Center the icons in the frame, so that scaling always happens in a visually consistent manner.
Setting up icon props
It’s a good idea to use variants in your icon library. For example, these could be two different components, like so:
Better yet, though, they could be one component definition, like so:
Here’s the changelog from the above:
- Combined them as variants, meaning there will only be one result returned for “book,” rather than two,
- Added a prop called “Filled,” set it to a boolean value (true/false) so that you can easily toggle it,
- Autolayouted the components to keep them neat, and easily maintainable,
- Added aliases by writing extra words in the “Description” field, pictured above — searches in Figma index the description field, meaning I can add aliases to improve discoverability in the asset search. You can add aliases like: synonyms, features the icons are associated with (e.g., “reader mode,” “scooter map,” etc.), or foreign language synonyms if you’d like to add convenience for any designers on any international teams you might have (if any).
Here is the result:
Things to avoid
- As mentioned earlier: overly specific naming. In this case, we have this icon called “No.” Most people would probably search something like “cancel” or “stop” or “prohibit.” If you really enjoy “no” as its name, that’s fine, but I’d recommend just passing those more common aliases in to the description field (e.g., stop, cancel, prohibit, disabled, etc.) for improved search discoverability.
- Unpredictable prop names. Before clicking into this dropdown (“Type”), I don’t know what this array will contain in it. “Type” is not descriptive, and I can’t easily derive what other options might be hiding in that dropdown.
Also, when you do click into the dropdown, the options are muddied. This contains two states: whether it’s filled (or not), and whether it’s an enabled icon (or not).
Instead, do this:
- Breaking out related icons too much. For example, here, we have two components: “frown” and “smile.” A designer would have to remember that these are separate icons, especially since the naming is so specific, like so:
Instead, do this:
This gives you:
- Combining icons in a way that is feature-specific. In general, people look for icons based on what the icon is (e.g., “sound,” “mute,” “apple,” “fruit,” “bird,” “cancel,” “sad,” “image”). So, let’s say, for our purposes, that your product has a feature that allows you to add and adjust imagery in some editor. Some might say, since these are all related icons that are used in that editor feature, they should all live in one component definition, like so:
But I would advise against this. The situation set up here means that, when a designer just wants to use the “image” icon in some other, unrelated context, they have to click through all these extra props. They might not even be certain that this image icon lives in here. Therefore, I might refactor these like so:
More specifically:
You can see that I’ve factored out this icon and gave it a bunch of aliases (you could add more, like “position,” and so on). If we want the designer to be able to discover this icon by searching for the “editor” feature, then just pass that in as an alias, rather than grouping all these icons together, reducing discoverability in turn.
Again, I’ve refactored this so that the “image” icon sits outside of that original component definition, as it’s a very broadly used icon in most products. We can include those contexts in its alias list (“editor,” for example), but this should live on its own for discoverability reasons, as mentioned earlier.
Ditto on this one, also factored out.
(A case, however, could be made for icons to be defined together if, for example, they will never be used in any other context, and they are highly specific. However, I would say this is rare, which is why I would discourage this.)
Components
Again, you have options for how to lay out your projects, so see the “Setting a foundation” section above. You can put your components all in the same place as your icons, or create another file just for them; you can have these files live in one project next to all your feature files, or in a project separate to all the other projects, or in an organization of its own (if you’re an enterprise customer). Regardless of your choice, you have to start thinking about how you want to group your components.
If you mainly support one platform
Supporting one platform means that you have a narrower filter, allowing you to group components by usage. For instance, a “Form Elements” page could contain things like buttons, inputs, etc. Another page, “Navigation” could contain all things related to navigational elements (e.g., footers, navs, etc.). And so on. If you have some responsiveness going on, you could set these as props.
If you support more than one platform
Supporting more than one platform means that it might make the most sense to group your components by platform, as that’s the largest meta-category across your components. For example:
You can see the way that I’ve set “Mobile,” where many patterns are likely to be shared between iOS and Android (navigation, etc.), for example, and “Web,” where functionality of navigation are likely to be irreconcilably different, such that it wouldn’t make sense to group them (their prop lists would become very long and hard to read). There are, of course, shared components, or extremely similar ones in terms of functionality and presentation (e.g., buttons, inputs, etc.), and I put these under “All” (can also be called “Shared,” or anything else you might prefer).
Constructing your components
The following are some best practices:
- Unless you have a specific use case to the contrary, always use autolayout. Here’s an example of why:
- Use nested components. Nesting your components allows you to make it easier to control repetitive components (“stay DRY” — with moderation), and reduce user error (inconsistent spacing between nav list items, etc.). Also, if, for some reason, a designer wanted to design a new nav with similar components, they could do so with the nested components you have provided.
Here’s another use case for nested components:
And another nested component use case:
- Nested component padding should be parent-agnostic, as they should be ready to use in many contexts. For this reason, I wouldn’t recommend setting padding on them unless you really need to (i.e., the component will never be used in any other context, the component actually does need the padding in order to work, etc.), like so:
On this issue, but on the implementation side, Gabe Scholz writes:
If [a component] has an internal
margin-right
, we will have to override it to0
. It effectively undoes the style rule back to the default browser value.
For this reason, I would suggest that parent components set spacing, where possible and needed.
- Make your components responsive. You might be setting suggested default sizes on your components (e.g., iOS 375W, Android 393W, etc.), but that doesn’t mean they need to break when designers deploy them in somewhat bigger/smaller contexts. Most of the time, you want things like text wrapping to work, so set “Fill” on most things, particularly where it concerns text. Like so:
- Add props you think designers will realistically use. This discourages them from breaking your components to dig into the markup and make their changes. Surface those variants on the parent, like so:
- Generally, order your props by most generalizable, to the least. There are cases where the readability suffers when trying to do this (i.e., two props may be closely related in functionality but have different scope, but it’s best to have them ordered closely due to their relation), so UYBJ. But here’s an example:
- Order your prop values nicely for increased readability, like so:
- Use good naming. This is how you’ll be able to get the most out of the asset search. For example:
Or with iconography:
Or the conventions I use for nested component naming:
Notice the way that the naming uses plain language, from “Nav” to “Nav List Item,” or “Bottom Nav” to “Bottom Nav List Item,” etc. I also do not include any special characters (e.g., /
, which has a specific usage to denote folder structure on export; nor any _
to denote an include, a concept from some web technologies; etc.). I want these to be highly readable both in the asset search and in the layer list. I prefer the asset search results to flow from most general components (“Nav”) to most specific ones (“Nav List Item”), alphabetically.
- Organize your icons and components. I like to use Organize Layers by Yuan Qing Lim, but you can use whatever you want. It makes it easier for your fellow library maintainers to consume and maintain.
- Components aren’t just skin-deep. When you’re making components, you need to think in a manner that’s more consistent with component functionality, rather than component appearance, most of the time. For instance, it might be compelling to view an errored toast and an errored modal as worthy of being in the same component declaration. For example, these two:
These are two, totally different components, however. Just because they look similar, and may even do something conceptually similar (break the flow of the page to “pop up” on the page in some way because they are both modals), they are not the same component: they’re triggered differently, are used by the user differently, are deployed in the codebase differently, and so on. These need to be two, different components, for this reason.
Additional thoughts
- I avoid sticker sheets in favor of building components. Designers want a workflow where they drag and drop components in, so I just optimize for that, and nothing else. Optionally, you can build presentation boards with your components, but, continuing on that thought:
- I avoid documentation inside of Figma, for numerous reasons (e.g., gets outdated quickly; most people do not read it; designers want to use the asset search, not a sticker sheet, so a presentation board is something they will not likely be seeing anyway; designers shouldn’t need documentation in one’s library to hit the ground running, but can optionally be added in to any text editing software of your organization’s choice; causes performance drops at scale for low ROI; frustrating to maintain longterm with all the layouting and styling; frustrating to add new components, have to make sure they’re in our presentation boards by repositioning stuff; etc.). I just list the components and keep them organized with a plugin. Figma is a vector graphics editor, not a text editor, and is unpleasant when used as one for the above listed reasons, so I don’t try to force the issue. And a library is a tool, not something that needs to consumed in a way that creates gravitas or esteem, so I don’t feel compelled to make presentation boards.
- Back to the idea of speedrunning, autolayouting is a great way to achieve that. By having as few elements on the page as needed (no documentation, presentation, no extraneous layers, etc.), you improve performance and load times on your file, making it a pleasant file to maintain. Also, by speedrunning components, you make those components easier to maintain and use. If a designer needs to break the component and make a new version of it, it’s easy for them to jump in and make changes without dealing with a ton of objects. Here’s an example of speedrunning a component:
In this case, we want to make a text input that has an underline that always takes up 100% of the width of the container, as shown. Here are two (of a few) ways to make this:
- On the left, we have a text layer that’s got padding on it using autolayout, on top of a rectangle (underline) set to
Fill
. To adjust the padding on this, you’d need to select the autolayouted child and manage it in there, - On the right, there’s a text layer that’s been autolayouted, and it has a stroke. This means that you can adjust the padding of this component on the parent, as the child has no attributes set on it. The stroke will always take 100% of the component because it’s just
border-bottom
.
But, as always, you need to weigh pros and cons against what you are looking to accomplish—if you wanted to have the underline have a rounded end, you’d have to use the left approach, as Figma doesn’t appear to allow you to set a round cap in this context.
File covers
As a side note, some people like to have covers on their files — not required, can be a nice touch, though. Here’s how I set mine up:
Here, there’s a cover card that has a page background color of your choosing. I use a page fill in Figma because previews can occur in many places in the product, and have different ratios, which also changes depending on your viewport size. For instance, a preview of this file in the project list of my organization has a rectangular ratio:
Or here, a preview card in the context of a single project, with files listed out, has a more square ratio:
In both of these cases, there are no gaps in these previews anywhere. This is because I’m setting the background color on the actual Figma page. In this case, I’m matching it to the cover card component (i.e., black), but you can pick whatever color you like. Figma picks the first page to render as a preview, so always do this on the first page in your file if you want a cover style to render.
I also generally maintain a cover card component, so that other teams can use this card in their cover pages, too. Here’s a quick example:
Final result
You get something like this. There’s much more I’d want to share, such as absolute positioning for things like “new” indicators on notifications, and things like this, but it’s gone on long enough! Happy to elaborate as needed. Anyway, thanks for reading!
Index
- Super Mario 64 Speed Run (5:34), by SpeedrunsDotNet. For my “speedrunning” metaphor when talking about minimal components,
- Vector icon speedruns, by Marc Edwards. An example of “speedrunning” as a metaphor, but applied to the making of iconography in as few steps as possible,
- Ant Design iconography, which is iconography I used throughout this guide,
- Figma in 5: Auto Layout, by Figma. A tutorial I linked throughout, as it’s a great tutorial in just minutes. Autolayout functions exactly like CSS Grid/Flex. I think you’ll like it very much,
- Why DRY is the most over-rated programming principle, by Gordon Cassie. I enjoyed this article because, though I think DRY (Don’t Repeat Yourself) is a useful concept in all systems thinking, including design component libraries, it can become a hindrance in some contexts,
- Notes on maintaining an internal React component library, by Gabe Scholz. I liked these thoughts, as I can personally relate to these reflections, having written and maintained component libraries in the browser, too; likewise, there is overlap between setting up component libraries in the browser and in Figma. The developer’s perspective on this can be useful for designers, I think,
- UYBJ, by Seth Godin. This is a concept I really enjoy,
- Organize Layers, by Yuan Qing Lim. This plugin is the GOAT.