NetcaneTechnologies

React

Design Systems in React: Tokens, Variants, and Component APIs

How to structure tokens, variant props, and component APIs so your design system stays consistent and scalable.

Yasir Haleem3 min read

A usable design system in React rests on three things: tokens for design decisions, variants for predictable options, and component APIs that are easy to use and hard to misuse.

Tokens: single source of truth

Tokens are variables for spacing, color, typography, and radius. Define them in one place (CSS variables, theme object, or a token file) and reference them in components. That keeps alignment with design and makes theme or brand switches a single change.

// tokens.js
export const tokens = {
  space: { 0: 0, 1: 4, 2: 8, 3: 16, 4: 24, 5: 32 },
  color: {
    primary: "var(--color-primary)",
    surface: "var(--color-surface)",
    text: "var(--color-text)",
  },
  radius: { sm: 4, md: 8, lg: 16 },
};

Use token names in components instead of raw values. That way “small” and “medium” stay consistent and design can change the numbers without touching component code.

Variants: a small set of options

Expose a small set of variants per component (e.g. size: small | medium | large, intent: primary | secondary | ghost). Map variants to tokens or styles via a config object or a small function. Avoid open-ended props like “className” as the only customization; combine a constrained variant API with an optional escape hatch (e.g. className or sx) for one-off overrides.

const buttonVariants = {
  size: {
    sm: { padding: "6px 12px", fontSize: 12 },
    md: { padding: "8px 16px", fontSize: 14 },
    lg: { padding: "12px 24px", fontSize: 16 },
  },
  intent: {
    primary: { background: tokens.color.primary, color: "white" },
    secondary: { background: "transparent", border: "1px solid currentColor" },
  },
};
 
function Button({ size = "md", intent = "primary", children, ...props }) {
  const styles = {
    ...buttonVariants.size[size],
    ...buttonVariants.intent[intent],
  };
  return <button style={styles} {...props}>{children}</button>;
}

Document the variant set and add new values only when design agrees; that keeps the system from drifting.

Component APIs: clear and constrained

Components should accept a small set of props that match how people think about the UI: “a button that’s large and primary” rather than “a div with these 10 classes.” Use TypeScript so variant values are autocomplete-friendly and invalid combinations are caught. Support composition (e.g. Button with an icon slot) instead of endless boolean props. Export the tokens and variant maps so other apps or themes can reuse them.

Summary

Base the system on tokens, expose variants for size and intent (and similar axes), and design component APIs that are clear and constrained. Keep tokens and variants in sync with design and use TypeScript and documentation so the system stays consistent and scalable.

About the author

Yasir Haleem is founder and lead engineer at Netcane Technologies. He builds production Next.js sites with headless CMS platforms — Strapi, Contentful, Sanity, and WordPress — with a focus on performance, SEO, and maintainable architecture.

Let's work together

Tell us about your project. We respond within one business day with a clear scope, timeline, and estimate.