Building Your First Component Library in Figma: A Step-by-Step Checklist

JP
FigmaPluginGuide
UX/UI Designer & Plugin Developer | 7+ Years Experience

Say you are trying to build a component library for a product that has three active feature teams, a backlog of design debt, and a stakeholder who keeps asking why the buttons look different across screens. You open Figma, create a new file, and start making components. Three weeks later you have 40 components, 12 of which are duplicates, a button with 6 separate variants that overlap, and no one on the team can find the right icon without asking in Slack. This post walks through the steps that prevent that outcome, ordered as a checklist you can work through top to bottom.


Symptom: The Library Exists But No One Uses It

Cause: Components were built in isolation, without a connection to the tokens, type styles, or grid systems that already exist in your file. The buttons look fine in the library file but don’t match the screens where they get dropped in.

Fix: Start with a token audit. Open your most representative production screen — the one that has been through the most design reviews. Write down every distinct color, spacing value, border radius, and font size. You will find duplicates: two grays that are one hex digit apart, three spacing values that are 8, 12, and 16 where 12 is only used once. Consolidate these into a single set of named tokens.

# Create a token file structure in your repo if you use code-first workflows
# This mirrors what you should define in Figma's local variables
tokens/
├── color.json        # primary, surface, text, border, status
├── radius.json       # sm, md, lg
├── spacing.json      # 4, 8, 12, 16, 24, 32
└── typography.json   # title-lg, title-md, body-lg, body-md, caption

In Figma, these tokens become local variables (Shift + I opens the Variables panel) or styles if you have not migrated. Wire the variables to your primitive components before building anything else. A button background, text color, and focus ring should reference variables, not hardcoded hex values. If you skip this step, changing a brand color later means updating 12 component instances individually instead of changing one variable.


Symptom: Components Keep Getting Detached

Cause: Instances get detached when the team edits a component and hits “Detach instance,” usually because the component’s variant structure does not cover the case they need. If a button only has primary and secondary variants, someone building a destructive action will detach the primary button and recolor it red inline. That red button is now invisible to your library’s variant system.

Fix: Design the variant architecture around anticipated states, not current usage. For a button, that means at minimum:

  • Variant 1: Type — primary, secondary, tertiary, destructive, link
  • Variant 2: Size — sm, md, lg
  • Variant 3: State — default, hover, pressed, disabled, loading

That combination produces 5 × 3 × 5 = 75 instances. You do not need to hand-place all of them — Figma generates the grid when you set up the variants. But you do need to handle the states that cause detaching: destructive is the most common omission, followed by loading.

If you are building a component that has fundamentally different layouts (a full-width card vs. a compact row card), that should be two components, not two variants. Variants are for changes in state or size, not for structurally different designs.


Symptom: The Icon Set Is a Graveyard of Inconsistency

Cause: Icons were added as individual SVG frames with different stroke widths, different 24px viewboxes mixed with 16px ones, and no naming pattern that sorts alphabetically in a useful way.

Fix: Standardize icon component properties. Every icon should be a single component with at least these variants:

PropertyValuesEffect
Size16, 20, 24, 32Scales the frame, stroke scales proportionally
Stroke weight1.5, 2Controls line thickness across all icons consistently
ColorUse variable referencesInverts correctly on dark backgrounds

Name icons with a consistent prefix and category: icon/action/add, icon/action/close, icon/navigation/arrow-right. In Figma’s asset panel, this sorts cleanly and lets you type icon/ to filter only icons.

A common failure is icons that scale with the frame but leave the stroke weight constant, which makes small icons look heavy. Set the SVG export options (Batch export in the right panel) to use the same export preset across all icons: PNG 1x and SVG.


Symptom: The Library File Is Slow and Unnavigable

Cause: Every component lives on one giant page with no separation between primitive, composite, and deprecated items. The file loads slowly, and finding a component takes longer than rebuilding it from scratch.

Fix: Structure pages as follows:

Page 1: Primitives
  - Buttons
  - Inputs
  - Icons
  - Forms

Page 2: Composites
  - Card (uses button + icon + text)
  - Modal (uses overlay + title + button row)
  - Table (uses row + cell + header)

Page 3: Deprecated
  - Anything that should not be used in new work, kept for reference during migration

Name components with a slash prefix to control asset-panel grouping: button/primary, input/text-field, modal/header. In the asset panel, the slash creates a folder structure that collapses and expands. Keep every component’s description field populated with a one-line usage note — it shows up as hover text in the asset panel and in the component detail view.

For performance, archive frames you are not actively editing. A library file with dozens of frames that all have complex fills and effects will lag in the Figma desktop app. Moving unused frames to a page named _archive (the underscore keeps it at the bottom of the page list) measurably improves navigation without needing to delete work.


Symptom: The Library Takes Too Long to Publish

Cause: You pushed the “Publish” button and waited while Figma processed hundreds of components, then realized you published the entire file including work-in-progress components that were never meant to be shared.

Fix: Use component sets with explicit publish permissions. In Figma, a component set can be marked as “Only this component” for publishing, which excludes all other components in the file. Set up a dedicated page named published/ where you move components that are ready for consumption. Components still in development stay on a wip/ page with publish permission set to “Do not publish.”

// If you use the Figma REST API for library validation (optional)
// Check which components are marked as published before syncing to your code repo
const published = await fetch(`https://api.figma.com/v1/files/${FILE_KEY}/components`, {
  headers: { 'X-Figma-Token': TOKEN }
});
const ready = published.data.meta.components.filter(c => c.containingFrame.pageName === 'published/');
console.log(`Publishing ${ready.length} components to code package`);

Symptom: Variant Properties Are Inconsistent

Cause: Button variants use a property called Type (capital T) while input variants use type (lowercase), and neither matches what your code team has in their component types. The Figma variant system is case-sensitive, so Type and type create two separate properties that behave identically but must be maintained separately.

Fix: Define a single naming manifest before creating any variants. Agree on capitalization, property ordering, and value naming with the team that consumes the library. A workable convention is:

  • Property names: Size, State, Variant, Type — all title case, no underscores
  • Value names: lowercase with hyphens — sm, md, lg, primary, secondary, destructive, loading

Write this manifest as a comment at the top of the primitives page so anyone adding a new component follows the same pattern. Inconsistency in property naming is the highest-frequency failure mode I have seen in team libraries; it is entirely preventable with a written rule that is visible where the work happens.


Symptom: The Library Is Not Connected to Code

Cause: The design team updates a component in Figma, but the codebase still has the old version. When the next sprint starts, the developer consumes the stale component, and the mismatch surfaces as a visual regression in review.

Fix: Use Figma’s Built-in Variables sync or export your token JSON into your design-token pipeline. If you are using the Figma REST API, you can fetch the latest styles and components after every publish and compare against your code package version:

#!/bin/bash
# Script to check for library drift between Figma and your code package
# Run this in CI after a library publish event

FIGMA_TOKEN="$1"
COMPONENT_KEY="$2"
CURRENT_VERSION=$(cat package.json | grep '"version"' | head -1 | awk -F'"' '{print $4}')
FIGMA_VERSION=$(curl -s -H "X-Figma-Token: $FIGMA_TOKEN" \
  "https://api.figma.com/v1/components/$COMPONENT_KEY" | jq -r '.version')

if [ "$CURRENT_VERSION" != "$FIGMA_VERSION" ]; then
  echo "Library drift detected: Figma has v$FIGMA_VERSION, code package has v$CURRENT_VERSION"
  exit 1
else
  echo "Library up to date"
fi

The drift check does not block a release — it just makes the mismatch visible before it becomes a bug report. Many teams run this as a nightly cron job rather than a blocking CI step, so the check surfaces as a Slack notification instead of a red build.


Symptom: The Library Grows Without Governance

Cause: A new component gets added every time a designer encounters a one-off layout need. Six months later, the library has 180 components, 60 of which could be composed from primitives using auto-layout.

Fix: Enforce a two-step review before a new component enters the published page. First, can it be composed from existing primitives with auto-layout? If yes, do not add a new component — document the composition pattern in the card’s notes instead. Second, does at least two teams need the same component? A single team’s special case belongs in their project file, not the shared library.

Track the usage of your components quarterly. In Figma, you can run an audit script against your team’s files to count instance usage:

// Simple usage count audit using the Figma REST API (requires team-level access)
const fileList = await fetch(`https://api.figma.com/v1/teams/${TEAM_ID}/files`);
const files = await fileList.json();
let usageMap = {};
for (const file of files.meta.files) {
  // Fetch each file's components, count instances in page hierarchy
  const pageData = await fetch(`https://api.figma.com/v1/files/${file.key}/nodes?ids=PAGE_ID`);
  const page = await pageData.json();
  // Recursively count node types === 'INSTANCE' and aggregate by componentKey
}

The output tells you which components are used widely and which have zero instances outside the library file. Zero-usage components are candidates for deprecation — move them to the deprecated page, not delete them immediately, since existing files may reference them.


Symptom: Migration from an Old Library Is a Manual Nightmare

Cause: Designers have to find every instance of the old button and swap it to the new component one by one, screen by screen, for three weeks.

Fix: Use Figma’s Swap Library feature. Select the old component instances (or use Edit > Find and replace across the file), then click the swap icon in the right panel and pick the new component. The swap works best when the old and new components share variant property names — this is where your naming manifest pays off. If the old component had a variant property Kind and the new one uses Type, the swap will mismatch and you will see 40 instances with broken variant values.

Mitigate this by aliasing. In the new component set, add the old property name as an alias property mapping to the new one. Figma supports property aliases since the 2024 update, letting you map Kind: primary to Type: primary automatically during swap. Test the swap on a copy of the file first, and always run the swap after the library is published so everyone sees the correct instances.


Final Verification: What a Healthy Library Looks Like

At the end of this checklist, run through these checks:

  • All primitive components reference variables, not hardcoded values. A button’s fill, text color, and border use variables from the token set.
  • Variant property names match the manifest exactly across all components — no Kind mixed with Type, no SIZE mixed with size.
  • Every component has a description field populated with a usage note and a link to the primary documentation page.
  • The asset panel sorts cleanly — type button/ and see only buttons; type icon/ and see only icons.
  • Publishing only contains what is ready. Work-in-progress components live on a wip/ page marked “Do not publish.”
  • The code drift check passes or there is an open issue tracking the mismatch.

The library is not done when it is built. It is done when the team can add a new feature screen without opening the library file at all — the asset panel has the pieces, the variants cover the cases, and the swap tool handles migration. That is the measurable outcome: new screens assembled from library components in less time than it takes to build one button from scratch.

Work through the checklist from the top — token audit first, then variant architecture, then publishing and governance. Which of the symptoms above is showing up in your current file? That is the next step to fix before adding any new components.

About the Author

FigmaPluginGuide is a UX/UI designer and Figma plugin developer with 7 years of design experience and several published plugins on the Figma Community, used by thousands of designers.