When you should use variants VS creating separate components

Before variants, the only way we could organize Figma components was using forward slashes or frames to group and nest them together. This was fine, but it wasn't great. It forced us into very rigid folder systems.

Folks often asked, "what's the best way to order groups of components?" Figma's documentation has an example with the following hierarchy: <span class="inline-code">Operating system / Component name / Device / Color theme</span>. But are there advantages to that particular order? Would it make a difference if it was flipped and “Color theme” came first? I always felt like slash-naming was important to keeping a file clean and organized, but that the organizational buckets I created were sort of arbitrary. Of all the combinations I tried, I was never happy with the groups or group-orders I came up with. Some were too convoluted, others were too loose to be useful. Finding that happy medium seemed impossible.

In late October of 2020 Figma introduced their variants feature 🙌. Not only was it a game changer for the experience of modifying instances in mockups and prototypes, it also gave us a new, even more powerful way to organize components: variants let us flatten the structures that slash naming forced us into. Variants allow all a component's, well, variations, to be accessed in any order. You no longer have to drill through a half dozen groups just to switch to dark mode.

2 years later Figma announced component properties in May of 2022 at Config. This feature was largely in response to “variant explosion”, a term Figmates use to describe enormous variant sets. I know I definitely authored a few too-big sets myself... my worst was probably a button with over 1,600 variants. Advanced users were quick to recognize the potential of component props, but felt it was lacking, and even causing confusion for their teams. After receiving this critique, Figma came through with a fast-follow release in the form of an open beta that any Figma user can participate in. This beta introduced 3 additional features to component properties:

  • Exposing nested instances (in my opinion, this was the biggest quality-of-life enhancement Figma has provided since releasing variants).
  • Simplified instances
  • Preferred values

But variants and component props haven't made slash naming obsolete. Nor were they meant to! You should use a combination of all of these features to organize your components and make them easy to work with. But along with all these new features come new questions to answer... like, when should you use variants VS component properties? Are certain components better to separate with slash naming than adding another prop using variants? When do you reach for one method over the other? Are there any circumstances where slash naming still has an edge over variants?

Because this is a long post, here are some anchor links to help you skip to sections you're most interested in:

Always use variants for:

Always use separate components for:

Always use component properties for:

Things you could use either variants or separate components for:

What about libraries?

<a href="#free-variants-separate-component-vs-component-properties-flow-chart" class="button secondary" style="padding:16px,0px; margin:auto;">Jump to free flow chart resource</a>

<h2 id="always-use-variants-for">Always use variants for:</h2>

<h3 id="interactive-states">Interactive states</h3>

E.g. "default", "hover", "active"

Figma had to release variants in order to release their interactive components feature. Variants were were born to handle interactive states like hover, active, and focus. Variants work best when the layer structure remains intact across everything in the set (if you're losing overrides, this might be why), which will be true in an  overwhelming majority of cases when you're dealing with interactive states. These manifest as as changes to color, stroke width, and effects like shadow. Without a doubt, you should always, always, always reach for variants when componentizing interactive states.

<h3 id="binary-states">Binary states</h3>

This is like a cousin of interactive states. The difference is that because binary states are, well, binary. They must be described in the property name because the toggle switch in the design panel can only communicate “yes/no”, “true/false.” When deciding what to name these properties, first think about sensible defaults. Like Nathan Curtis talked about in his Schema 2022 talk, your instance users might prefer to have all options turned on and then prune away what they don't need. Others may appreciate the opposite, where they add things onto a very basic composition. With your user’s preferences in mind another nice touch is phrasing your binaries in the affirmative state so you have a match between the property name and the "true” value. Here’s some examples:

  • Any component with an empty state, I’d name the property <span class="inline-code">Populated</span> (rather than <span class="inline-code">Empty</span>)
  • A component that communicates whether we’re seeing a logged out or logged in experience. I’d name the property <span class="inline-code">Logged in</span> rather than <span class="inline-code">Logged out</span>
  • A component that has a disabled state. Some folks choose to lump this in with interactive state, but I personally think it makes more sense a distinct property. The <span class="inline-code">state</span> variant you’d pair your <span class=”inline-code”>Enabled = true</span> would be “Default.”

<h3 id="size">Size</h3>

E.g. Gmail’s “Default”, “Comfortable”, “Compact” for items in your inbox.

I want to be clear: when I’m talking about “size” I’m talking about how much space that component occupies. I see some folks use “size” when they mean breakpoint, which I go over later in a section covering things you could use variants or separate components for.

So what exactly is size?

In my view, size means you’re dealing with the same anatomy and composition, but things like padding values and space-between values can change. Larger values means the component will exist at a larger size, and vice versa.

I think the reason folks confuse size and breakpoint is because they do tend to overlap... the larger version of your <span class="figma-component">◈ Button</span> on a marketing landing page is likely used at larger breakpoints, and the smaller ones at smaller breakpoints... but that won’t always be the case. A complicated web-app with tables and tons of controls might benefit from using <span class="figma-component">◈ Button</span> at it’s smaller sizes to accommodate a denser interface, despite being used at a larger breakpoint.

Another common example for my team is using a “size” variant property to choose different padding values on our <span class="figma-component">◈ Card</span> components. Yes, we could offer a single card and have designers override the padding, but variants reduce the cognitive load they take on (a win for component usability). It also is more opinionated: a select-list of options implies “these are the constraints we agreed we all should design within when using this component”, while letting designers override values means they have to either memorize those values, or reference documentation to know how to follow the system.

Similarly, “size” also gets confused with “device.” And even if you've got your definitions straight, design system experts like Molly Helmuth (founder of UI Prep) considers focusing on device over breakpoint is a mistake, which she sees many people making:

<h2 id="always-use-separate-components-for">Always use separate components for:</h2>

<h3 id="icons">Icons</h3>

E.g. <span class="figma-component">◈ Icon / gear</span>, <span class="figma-component">◈ Icon / house</span>, <span class="figma-component">◈ Icon / pencil</span>

Bonnie Kate Wolf wrote a massively helpful piece for DesignSystems.com titled "A complete guide to iconography." You should bookmark this one. There's not a publication date on the article, but I'm pretty confident it was written before Figma introduced their variants feature, because she describes only using slash naming, frames, and pages to organize icon components, despite using the word "variant" four times in the article (kinda funny, it's like she predicted the feature name)!

Icons are things that you absolutely want to organize as separate components. I’ve seen folks make one big "icon" component that has a property named "name" with dozens (or hundreds!) of variants inside like "light bulb", and "stopwatch" in it. Why is that set up bad? Here's a quick list of drawbacks:

  • ❌ No visual preview
  • ❌ No combo box to filter by name
  • ❌ Every icon variant will be rendered in the background when you drop one of these on the canvas (Figma’s documentation on this). This is what makes it possible to switch between variants instantaneously, even if you lost your internet connection.

“What about size?” If you need to offer icons at different sizes, I strongly recommend leveraging Ridd’s icon-wrapper method.

“What about interactive states?” Sure! If it’s the same icon, then that makes perfect sense. Molly also endorses this approach.

<h3 id="HTML-tags">Foundational atoms and molecules with unique HTML tags</h3>

Eventually, engineers are going to reference your Figma components so they can build the product in real life. As much as we can align our Figma components to the structures of the real web, built on HTML, the less translation work we create for our developer colleagues.

HTML tags I try to watch out for include: <span class="inline-code">button</span>, <span class="inline-code">footer</span>, <span class="inline-code">input</span>, and <span class="inline-code">label</span>

<h3 id="interface-reps">Different ◈ interface representatives</h3>

E.g. <span class="figma-component">◈ Header</span>, <span class="figma-component">◈ Footer</span>, <span class="figma-component">◈ Button</span>, <span class="figma-component">◈ Card</span>

No one would combine their global navigation and footer as variants in a component named "global elements." They're separate chunks of interface that do different jobs, therefore they ought to be separate components.

If you’re curious why I’m referring to these as “interface representatives” and not just “components”, and what the deal is with the ◈ icon, you’re in luck! I’ve got a blog post all about it.

<h3 id="placeholders">✂︎ Placeholders</h3>

These components allow you to "edit" an instance's layers without detaching. If you haven't heard about these bad boys, watch Adam Przewoski's youtube tutorial about them.

<h3 id="base-components">⍚ Base components</h3>

Remember, you’re better off not using ⍚ bases in your component libraries! Instead use them in local files. And naturally, they’re going to be their own separate components in order to be of any use to you.

<h3 id="content-carriers">✎ Content carriers</h3>

Similar to ◈ interface reps, ✂︎ placeholders, and ⍚ bases, ✎  content carriers have to be separate components.

<h3 id="starter-kits">⍂ Starter-kits</h3>

Starter-kits are curated collections of instances of other components. They're fully intended for to be detached, therefore you want them to be their own components.

<h2 id="always-use-component-properties-for">Always use component properties for</h2>

<h3 id="minor-composition-changes">Minor composition changes</h3>

Component properties’ whole deal is about boosting instance usability. They drastically reduce the number of scenarios where users have to deep-click through the layer tree to make modifications to an instance’s composition. Changes such as:

  • Showing and hiding layers
  • Swapping nested instances for other instances (like icons or ✎ content carriers)

Okay, before charging ahead to the next section let’s recap. I covered the absolutes:

circumstances where you should definitely use variants:

  • interactive states
  • binaries
  • size

circumstances where you should definitely use component properties:

  • minor changes to composition

circumstances where you should definitely use separate components:

  • things that map to an HTML tag
  • icons
  • device / operating system

But the next section is where the waters get murkier. There will be strong arguments you could make in either direction, making it difficult to say “this is a universal best practice.” Know that the recommendations I make are very much colored by the preferences of the designers and engineers I’ve worked with, the products we’ve worked on, and probably some subconscious stuff that’s not even on my radar. But I’ve done my best to narrow the options to present you with two strong paths, and some prompts you can use to help make it clear which path is best for you and your component users.

<h2 id="either-variants-or-separate-components">Things you could use either variants or separate components for!</h2>

With every fork-in-the-road presented in this section, I encourage you to reflect on the following topics alongside your component users.

  • Parody between Figma component and coded component: How much does this matter to your team? Are there certain components where it’s especially important, or less important?
  • Sensible defaults and everyday usage: when you encounter a “variants VS separate components” decision, take some time to think about the experience of using instances of that component. Are there attributes of the component that designers are tuning more frequently than others? Frequent adjustments may benefit from variants, whereas swapping between components might be better for infrequent adjustments. If the component already exists and you have access to your library analytics, see which variant get the most usage and make consider making it the default (if it isn’t already).
  • Instance volume and performance: How many instances of this component will be used in a single local file? A dozen? A thousand? This matters if you're concerned about file performance. Figma's documentation warns us that when we add an instance with variants to a file, Figma will load every variant in the component set. Importing large component sets will impact speed and performance even if you’re using a spec’d out machine. Remember, we’re dealing with browser memory (which caps at 2GB), not your computer’s. Using separate components could offer designers relief in laggy files, especially if it compliments their everyday usage.

<h3 id="resizing-rules"><strike>Resizing rules</strike></h3>

E.g. "hug", "fixed", "fill"

My first publication of this post included a suggestion about componentizing resizing rules. Now that I’m re-writing this, I can say that not only do my team and I not use this tactic, but I’ve yet to hear of other teams or Figma enthusiasts using it. I'm keeping this section for anyone who might've followed my advice in the past who is reading this again.

To be honest, this was a crutch I developed by using base components in our UI library, which I no longer recommend. If you’re curious about the prior version of this part of the post you can check it out on wayback machine.

<h3 id="hierarchy">Hierarchy</h3>

E.g. "primary", "secondary", "tertiary"

Most folks tend to handle hierarchy as a variant property, and it makes tons of sense: most of the time hierarchy is presented as changes to things like spacing, colors, effects, strokes, but the anatomy stays the same. Variants are a great fit for that reason, and they’d be my recommendation.

However, if you're worried about a the total number of variants stacking up, you could choose to handle hierarchy using separate components. Remember from earlier in this section: consider how folks will use this component. If they're constantly trying out different hierarchy levels with the component, variants make doing that work go faster. But if your component users know which hierarchical level they need from the jump, separate components could help with file performance.

<h3 id="validation">Validation</h3>

E.g. "default", "success", "error", “warning”

Form elements are probably the first thing that came to mind for you when you think about validation. These guys are notorious for inflating the total variant count. Like buttons, form elements are richly interactive pieces of interface, but unlike buttons they can have tons of atom-sized objects hanging off of them: helper text, character count, icons with tooltips, labels with icons, icons that precede the input box, icons that trail after the input box... now multiply all of that on top of interactive states, states of ability, and sometimes resizing rules 🥵 whew!

Before we had component properties, form elements were a classic example I’d use when describing components that were often at risk of having a total variant count that ran super high. Luckily a lot of what I listed can be mitigated with component props, because they are minor changes to the form element’s composition. On the one had, validation is tricky in the same way hierarchy is: validation styling often involves a changes to color and effects (a great reason to reach for variants), but unlike hierarchy sometimes those minor composition changes (like revealing a check icon to drive home a “success” validation styling) are required to be along for the ride.

So yeah, you could handle this like hierarchy: use variants to handle changes to styling and component props for the minor composition changes. This approach means your total variant count will stay slim, but it also means designers need to remember to reveal that icon or that helper text manually. Is there another way? Yeah! Separate components. If designers aren’t using validation styling in their mocks or prototypes super often, or you’ve got more than 3 validation states to manage, then you could demote the validation styles from being a variant property and instead handle them as separate components. You could do this with slash naming: <span class="figma-component">Form elements / ◈ Text area / Error</span>. Talk it through. as a crew.

<h3 id="breakpoint">Breakpoint</h3>

E.g. “1440px and above”, “756px to 1439px”, “755px and below”

I used to couple breakpoint and device, but no more. These are separate things. Breakpoint is all about enhancing component responsiveness at different viewport widths. Using auto layout on your components helps a lot with this, but for larger ◈ interface reps eventually you’ll need to change the composition. For example, elements that are arranged to sit side-by-side horizontally eventually are so squeezed for space that they need to be arranged in a grid, or vertically:

responsive design.gif
source: Dribble shot by Matt Plays

I’d recommend using variants if the following were true:

  • The layer architecture can remain the same, it’s just the auto layout direction and resizing rules that need to change in a few places (e.g. things running horizontally change to vertical to accommodate smaller breakpoints).
  • The total variant count is at or under 12. Why 12? Having a breakpoint property means you’re at least doubling the total variant count. But potentially tripling it. Quadrupling in the cases of more complex organism-size components! So worst-case scenario, you need to support 4 breakpoints, you already have 12 variants... 12 multiplied by 4 means 48 total variants... and you can see how this quickly becomes a hefty variant set.
  • No more than 3 unique breakpoints need to be added.

But outside those constraints that, I’d begin advising you to think about using separate components to handle breakpoint. As described with validation text, these could be named and organized like so <span class="figma-component">Free download call-to-action / 1440px and above </span>.

<h3 id="major-composition-changes">Major composition changes</h3>

E.g. a product card that shows up in many many different contexts

This is a big one. You might have a single component that requires very different layer compositions. Sometimes those compositions might be context dependent, like how Wirecutter displays products on their site. The page or section that a product is shown in changes what information about the product is displayed. Here are three examples:

On the home page there is a "Deals" column. Recommended products that have recently experienced a drop in price will appear here. It's a small space, so there's not a lot of room for detailed information. The photo, product name, price, seller, and savings is all that fits.

image.png

If you proceed to Wirecutter's Deals page, you'll find products are contained within cards and have a lot more information in them! Here we have detailed explanations about why Wirecutter recommends this particular product, along with the article this product is featured in, and a timestamp of when the deal had dropped.

image.png

Products also get featured in cards in Wirecutter's articles and guides. This is where the card design gets more complicated: there's a blue banner hanging off the top left, and a small beak on the bottom left. The description in the card is shorter here than on the Deals page because the editorial content is handled by the article. Instead, cards on article pages contain more interactive components like buttons and a bookmark icon. The orientation has changed too; the product image sits to the left of the buttons and description instead of above.

image.png

These are all the same component, and there’s a lot of mainstay anatomical pieces such as the product name and image. Because of that you might think it makes sense to make them all variants within a single component named <span class="figma-component">❖ Product card</span> with a property called “on page” with options like “home”, “deals”, and “article”. And you could! But my opinion is that they differ enough in anatomy and visual design I'd probably opt to keep them separate components. I actually decided to build  these components out in Figma myself:

Here's a link to the community file for the Figma file above. The blue ribbon on that "article feature" product card was tough to figure out (I made this before Figma released absolute positioning). But it's up and running!

<h2 id="libraries">What about libraries?</h2>

The question so far as been "when should you use variants, when should you make separate components, and what about component properties?", and that's all been in the context of a single Figma library file... but there's a fourth thing to throw into the mix: having multiple separate libraries.

Why do this? Well, if you're familiar with swapping components and the benefits that offers, take that same concept and apply it to libraries. Figma's help doc on library swapping has three examples where library swapping is handy:

Swap libraries lets you to quickly update all instances in a file, so they use components from another library. Use swap library to:
Use a different or newer version of a library
Swap to external or agency-provided libraries
Manage instances from missing or unpublished libraries

Take Netflix for example: their design team works out of multiple component libraries to produce the app many of us enjoy. That app needs to work on our TVs, on our phones, tablets, and in our browsers. Instead of working out of one big library and handling "device" as a variant property, or even as separate components, they chose (wisely, I think) to handle device at the library level.

Another choice they made that helps keep the Netflix app experience uniform across those three very different devices is underpinning their device-specific libraries with a library called "Foundations." My understanding of their Foundations library is that it feeds components and styles into their device specific libraries. Overrides might get made to those Foundations components to account for device specific needs (text on the TV is likely much bigger than mobile devices), but this is what helps create that cohesive Netflix experience.

Annotated screenshot from the Netflix team's Schema 2021 talk
Annotated screenshot from the Netflix team's Schema 2021 talk

That's just one way to do it though. Your team might have different needs. Maybe your team only designs for mobile, but offer four different color themes... you could handle those themes with separate libraries!

<h3 id="device-OS">Device/Operating system</h3>

E.g. "iPhone" VS "Android", “MacOS” VS “Windows”

This is one that is truly dependent on how you/your team works and what experiences your product offers. Some product organizations have entirely separate teams of designers working on desktop and mobile experiences, and they might maintain separate libraries. Or everyone could be merged together, with all designers expected to mock up, prototype, and spec out experiences across all devices. Netflix's designers warn against structuring your design system on your team structure:

"One trap we should all avoid is coming up with a design system that is shaped around a team, because a team could very well change. Instead a design system should be here to stay." Luca Orio - Schema 2021

<h3 id="style">Style</h3>

E.g. "filled", "stroke"

Going back to Wolf's article  Remember, this was very likely to have been published pre-variants: when it comes to different styles of the same component (in this case, icons), she says:

"When you have the same icon with variants, here's how I like to handle them...
Filled vs. stroked: If you are using both styles, use a slash after the icon name to indicate filled or stroked icons."
image.png
The same icons drawn in different styles. Source: Wolf’s article

Like breakpoint, whether you handle the "style" of a component as a variant, as separate components or in a different libraries depends on your needs and the need of the product you're designing for. Talk this one out with your team!

<h3 id="theme">Theme</h3>

E.g. "dark mode"

Figma's library swapping feature was made with this in mind. Similarly to how variants were made to support interactive components, library swapping was made to support theming. Because of this, I’d recommend using libraries to handle your theming if you’re trying to support something like “dark mode.”

Quick note: this is different from “zebra stripe” design. I think this style is on it’s way out, but an example would be having high-contrast sections stacked one on top of the other. It’s tempting to call the variation of components that sit within the dark background sections “dark mode”, but that’d be a misnomer. Brad Frost explains this well in his blog post about dark mode VS inverted colors:

What is actual dark mode? <span class="inline-code">prefers-color-scheme</span> is a CSS media feature that listens to a user’s preference for dark or light mode at an operating system or user agent level ... There’s another thing that sometimes gets called “dark mode”, but really means “this component when rendered on a dark background” ... To ensure components work on a dark background, we’ve established a naming convention called <span class="inline-code">inverted</span>.

While I can't speak from personal experience (yet to work on a product that themes their interface), I'd be inclined to use Figma's library swapping solution for this.

<div class="horizontal-rule"></div>

Phew. Okay... so now what? How do you take this stuff and actually apply it? Well, it'll take some practice and experimentation, especially with those fork-in-the-road decisions. I could tell you to bookmark this post, but this is a 16 minute read! I wrote this to provide in-depth explanations about why certain decisions are sure-fire, and what to consider when you're weighing two or more options. This post is not a quick reference by any means. So I made you one! Below you'll find a flowchart built in FigJam to quickly get the same information in a visual format, right inside Figma.

<p id="free-variants-separate-component-vs-component-properties-flow-chart"></p>

{{free-variants-separate-component-vs-component-properties-flow-chart}}

Between this blog post and that FigJam file you should be able to not only make quick decisions, but strong, accurate ones. After some practice you'll have all the absolutes memorized, and navigating the "this or that" decisions will get easier each time. Especially if you're working with other designers to talk through what's best for you all as a group. And as you're trying out these strategies, tell me how it's going! I'd love to hear what's working, and even more what doesn't work. I intend to follow the same advice myself, and keep this post updated as my thinking evolves.