Building Scalable Design Systems with React and Tailwind

December 1, 2024

Design systems are the backbone of consistent user interfaces. Here's how to build one that scales—along with a solid project structure to support it.

Why Design Systems Matter

A well-crafted design system provides:

  • Consistency across all products
  • Faster development with reusable components
  • Better collaboration between designers and developers
  • Reduced technical debt over time

Core Principles

1. Start with Tokens

Design tokens are the atomic values of your system:

export const tokens = { colors: { primary: { 50: '#eff6ff', 500: '#3b82f6', 900: '#1e3a8a', }, neutral: { 0: '#ffffff', 100: '#f5f5f5', 900: '#171717', }, }, spacing: { xs: '0.25rem', sm: '0.5rem', md: '1rem', lg: '1.5rem', xl: '2rem', }, radii: { sm: '0.25rem', md: '0.5rem', lg: '1rem', full: '9999px', }, } as const;

2. Build Primitive Components

Start with the basics:

import { cva, type VariantProps } from "class-variance-authority"; const buttonVariants = cva( "inline-flex items-center justify-center rounded-md font-medium transition-colors", { variants: { variant: { primary: "bg-primary text-white hover:bg-primary/90", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", }, size: { sm: "h-8 px-3 text-sm", md: "h-10 px-4", lg: "h-12 px-6 text-lg", }, }, defaultVariants: { variant: "primary", size: "md", }, } ); interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {} export function Button({ variant, size, className, ...props }: ButtonProps) { return ( <button className={buttonVariants({ variant, size, className })} {...props} /> ); }

Component Composition

Build complex components from primitives:

LevelExamplesPurpose
TokensColors, spacing, typographyFoundation
PrimitivesButton, Input, BadgeBuilding blocks
PatternsCard, Modal, DropdownCommon UI patterns
TemplatesPageHeader, SidebarLayout structures

Project Structure & Setup

Framework

  • Nextjs

Libraries

  • ShadCn
  • Prettier
  • Eslint
  • Zod
  • Dnd-kit
  • Tailwind-css
  • React-markdown
  • Copilotkit
  • Driver.js
  • ReverseUI (animations)
  • Framer motion
  • React360 (vr website)
  • Swiper

Imports structure

  • React stuff
  • composants
  • Hooks & messages and css

Function structure

  • State variables
  • Hooks
  • Callbacks
  • Variables
  • Functions
  • UseEffects

Folder structure

Section like admin/user/super admin/dashboard

Components

  • Components/atoms
  • Components/molecules (uses atoms)
  • Components/section (uses molecules)
  • Components/<comp-name> (uses sections)
  • Components/ui (optional, external stuff like shadcn)

Pages

  • ?Pages/section/section-name?
  • Pages/<PageName>/<page-name>

Layout

  • Layout/section/<section-name>

Hooks

  • Hooks/section/<hook-name>

Context

  • Context/section/<context-name>

Utils

  • Utils/<util-name>

Libs

  • Libs/<lib-name>

Boiler plate

  • 404 not found page
  • Landing page
  • About us page
  • Contact page with form
  • Dashboard with draggable widgets and charts

Documentation is Key

"A design system without documentation is just a component library."

Every component should include:

  1. Usage examples - Show common use cases
  2. Props documentation - Explain all options
  3. Accessibility notes - ARIA labels, keyboard nav
  4. Do's and Don'ts - Guide proper usage

Versioning Strategy

{ "name": "@company/design-system", "version": "2.1.0", "peerDependencies": { "react": "^18.0.0", "tailwindcss": "^3.0.0" } }

Use semantic versioning:

  • Major: Breaking changes
  • Minor: New features (backward compatible)
  • Patch: Bug fixes

Conclusion

Building a design system is an investment that pays dividends in:

  • Developer productivity
  • Design consistency
  • User experience
  • Team collaboration

Start small, iterate often, and document everything.

GitHub
LinkedIn
X
youtube