React Project Folder Structure

One pattern I’ve found effective is separating core design system components from application-specific components. This separation helps maintain a clean, scalable architecture, especially as the project grows.

Design System Components: These live in a global components folder. They are reusable, generic, and contain no business logic. Think of components like Button, Card, or Modal - building blocks that could theoretically be reused across multiple projects.

Feature-Specific Components: Components tied to a specific feature, such as a dashboard or profile page, are kept within that feature's folder. For example, the DashboardHeader and StatsCard components would live in a dashboard directory.

Shared Components: Components that sit in the middle ground between global and feature-specific (eg. a Header used across multiple pages) go into a shared folder. These are not part of the design system but are reused across multiple parts of the app.

Here's an example folder structure for a Next.js project following this approach:

/app
├── {feature}               # Feature-based folders (eg. profile, dashboard)
   ├── components          # Page-specific components for this feature
   └── page.tsx            # Feature-specific page
├── shared                  # Shared components used by multiple features
   ├── components          # Shared components (eg. Header, Sidebar)
   └── layout.tsx          # Global layout
└── page.tsx                # Root-level page

/components                 # Global reusable components (design system)
├── Button
   ├── Button.tsx          # Component file
   ├── Button.test.tsx     # Component test file
   └── Button.stories.tsx  # Storybook story file
└── index.ts                # Barrel file for easier imports

/styles                     # Global styles and design tokens
└── global.css              # Global CSS file importing Tailwind CSS

/public                     # Static assets (eg. images, fonts)

Example folder structure within a nextjs project

Why I Like This Structure

  1. Scalability: As the app grows, keeping components co-located with their feature reduces clutter in the global components folder and makes it easier to maintain feature-specific code.
  2. Reusability: Shared and design system components are centralised, ensuring consistent styling and functionality across the app.
  3. Clarity: By grouping components by purpose (global, shared, feature-specific), it makes it easier to understand where a component belongs.

It has it's challenges

Like any approach, this one has challenges.

The Middle-Ground Component Confusion. Sometimes it's not immediately clear whether a component belongs in shared or the design system. My advice? Start components in the shared folder and only move them to the design system if they become truly reusable.

Features Can Get Messy. Over time, a feature folder can become cluttered with too many components. When this happens, you can refactor and create subfolders to stay organised. However, avoid premature optimisation!

Got thoughts on this approach? Let me know over on Twitter!

← Back to all writing