KIQA.DEV
HomeServicesPortfolioBlogAboutContactDEV HUB
Get a Quote
KIQA.DEV

Professional development services.

ServicesPortfolioBlogAboutContactDev Hub
© 2026 KIQA DEV. All rights reserved.
CVBuilt with Next.js & TypeScript
Blog
Design8 min read · Feb 8, 2026

Building a design system for a 300-component React Native app

When your app has 300 components and 3 developers, design tokens aren't optional. This is how we built ours.

Why We Needed a Design System

Spindare hit 100 components before we had any formal system. Everything worked, but opening a new screen and trying to match the color of a button from a screen built two months ago meant digging through files, copy-pasting hex values, and hoping nothing drifted.

By 200 components, the drift was visible. Slightly different border radii, subtly inconsistent spacing between interactive elements, three different shades of "gray" that were all meant to be the same thing. On a phone screen, these details matter.

At 300 components across three developers (me, my uncle, and Daniel), a design system wasn't optional. It was either invest a week in building one or spend months fixing visual inconsistencies one by one.

The Token Layer

We started at the bottom: design tokens. Every visual constant goes here before it goes anywhere else.

// tokens/colors.ts
export const colors = {
  background: {
    primary: '#0A0A0F',
    secondary: '#13131A',
    tertiary: '#1C1C26',
    elevated: '#222230',
  },
  accent: {
    primary: '#F5A623',
    soft: 'rgba(245, 166, 35, 0.15)',
    border: 'rgba(245, 166, 35, 0.3)',
  },
  text: {
    primary: '#FFFFFF',
    secondary: 'rgba(255,255,255,0.65)',
    tertiary: 'rgba(255,255,255,0.35)',
    muted: 'rgba(255,255,255,0.2)',
  },
  // ...
} as const;
// tokens/spacing.ts
export const spacing = {
  xs: 4, sm: 8, md: 12, lg: 16,
  xl: 24, xxl: 32, xxxl: 48,
} as const;

export const radius = {
  sm: 6, md: 10, lg: 16, xl: 24, full: 9999,
} as const;

The as const is important — it gives TypeScript literal types so you get autocomplete and type errors if you use a token that doesn't exist.

The Component Layer

On top of tokens, we built a set of primitive components that all other components compose from. The most important ones:

CODE_START<Box />CODE_END — basically a View but it accepts spacing/color tokens as props:

<Box px="lg" py="md" bg="secondary" radius="md">
  {children}
</Box>

CODE_START<Text />CODE_END — a typed text component with preset variants:

<Text variant="heading" size="xl">Post title</Text>
<Text variant="body" color="secondary">Post content</Text>
<Text variant="label" color="muted">3 min ago</Text>

CODE_START<Pressable />CODE_END — wraps React Native's Pressable with standard feedback styles, haptics, and disabled state handling baked in.

Every other component in the app is built from these three primitives. When we want to update how disabled states look globally, we change it in <Pressable /> and it propagates everywhere.

Keeping It Consistent: The Rules

Rules that have saved us the most time:

1. No raw hex values outside of tokens. If you need a color that's not in the token file, add it there first. Never write #F5A623 in a component file.

2. No CODE_STARTStyleSheet.createCODE_END for one-off styles. If a style is used once and can be expressed with tokens via Box/Text, do that. StyleSheet.create is for reusable, named styles only.

3. Every new component gets a Storybook story. We use Storybook for React Native to preview components in isolation. This caught probably 30% of design bugs before they ever hit a real screen.

4. Spacing only from the scale. No marginTop: 13. If 12 or 16 doesn't work, the layout needs to change, not the spacing value.

What Saved Us the Most Time

Honestly: the Text component variants. Typography inconsistency was our biggest problem before the system. Having a fixed set of variants (heading, subheading, body, label, caption, mono) and enforcing that everything uses them meant we stopped having "close enough" font sizes scattered through the codebase.

The second biggest win: the token file as source of truth for dark mode. Because everything references tokens, adding a second theme was just adding a second set of token values and a context switch. No hunting through components for hardcoded colors.

Where We'd Do It Differently

Start earlier. We built the design system at component 200. Starting at component 1 would have saved a lot of retroactive work converting old components to use tokens. Even a minimal token file and two primitive components from day one is infinitely better than adding them later.

BlogGet a quote