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.

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.