Skip to content

React Folder Structure — Best Practices for Scalable Projects

DodaTech 8 min read

In this tutorial, you'll learn about React Folder Structure. We cover key concepts, practical examples, and best practices.

React folder structure decisions at project start determine whether your codebase stays navigable at scale or collapses into tangled imports and dependencies.

What You'll Learn

Organize React projects using proven folder structures — feature-based vs type-based layouts, atomic design principles, shared component patterns, route layouts, and path alias configuration.

Why It Matters

A bad folder structure is the #1 cause of "I'm afraid to touch that file" syndrome. When every new feature requires hunting through five folders, developer velocity drops. Good structure makes onboarding, refactoring, and scaling predictable.

Real-World Use

A SaaS dashboard with 50+ screens, shared UI components used across features, and a team of eight developers — where every person can find the right file without asking. This same architecture powers Doda Browser's settings panel and Durga Antivirus Pro's quarantine management interface.

Type-Based vs Feature-Based Structure

Two dominant approaches exist. Let's compare them side by side:

Type-Based (flat by file type):    Feature-Based (grouped by domain):
src/                                src/
├── components/                     ├── features/
│   ├── Button.tsx                  │   ├── auth/
│   ├── Card.tsx                    │   │   ├── LoginForm.tsx
│   ├── Modal.tsx                   │   │   ├── RegisterForm.tsx
│   └── Header.tsx                  │   │   └── AuthContext.tsx
├── hooks/                          │   ├── dashboard/
│   ├── useAuth.ts                  │   │   ├── StatsGrid.tsx
│   └── useFetch.ts                 │   │   ├── ChartWidget.tsx
├── pages/                          │   │   └── DashboardLayout.tsx
│   ├── Home.tsx                    │   └── settings/
│   ├── Login.tsx                   │       ├── ProfileForm.tsx
│   └── Dashboard.tsx               │       └── ThemePicker.tsx
└── utils/                          ├── shared/
    ├── api.ts                      │   ├── Button.tsx
    └── helpers.ts                  │   ├── Modal.tsx
                                    │   └── Card.tsx
                                    ├── hooks/
                                    │   ├── useAuth.ts
                                    │   └── useFetch.ts
                                    ├── layouts/
                                    │   ├── MainLayout.tsx
                                    │   └── AuthLayout.tsx
                                    ├── pages/
                                    │   ├── Home.tsx
                                    │   └── NotFound.tsx
                                    ├── services/
                                    │   └── api.ts
                                    └── utils/
                                        └── helpers.ts
Aspect Type-Based Feature-Based
Finding a file Know its type first Know its feature first
Scaling to 50+ files Becomes cluttered Each feature stays small
Code ownership Unclear Feature owner is obvious
Refactoring a feature Touch 8 folders Touch 1 folder
Shared code reuse Easy to find Need shared/ folder
New developer onboarding Must learn all types Learn one feature at a time

Recommendation: Start with feature-based once you have more than one developer or more than 20 components. React Tutorial for Beginners often uses type-based because example apps are small, but real projects outgrow it quickly.

Atomic Design Structure

Atomic design by Brad Frost maps well to React components. Here's how React teams implement it:

src/components/
├── atoms/          # Smallest UI units
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.test.tsx
│   │   ├── Button.module.css
│   │   └── index.ts
│   ├── Input/
│   ├── Label/
│   └── Icon/
├── molecules/      # Combinations of atoms
│   ├── SearchBar/       # Input + Button + icon
│   ├── FormField/       # Label + Input + error
│   └── Card/
├── organisms/      # Complex sections
│   ├── Header/          # Logo + Nav + SearchBar
│   ├── Footer/
│   └── DataTable/
├── templates/      # Page layouts
│   ├── DashboardTemplate/
│   └── AuthTemplate/
└── pages/          # Actual route pages
    ├── HomePage/
    ├── LoginPage/
    └── DashboardPage/

Each component folder contains the component, its test, its styles, and a barrel index.ts export:

// components/atoms/Button/index.ts
export { Button } from "./Button";
export type { ButtonProps } from "./Button";

Why this works: A SearchBar molecule (Input + Button + handler logic) changes independently from where it's used. You can test it in isolation. You can reuse it in the Header organism and in a MobileSearch organism.

Shared Components Pattern

Not everything is feature-specific. The shared/ directory holds reusable primitives:

src/shared/
├── components/
│   ├── Button/
│   ├── Modal/
│   ├── Spinner/
│   └── Toast/
├── hooks/
│   ├── useDebounce.ts
│   ├── useLocalStorage.ts
│   └── useMediaQuery.ts
├── utils/
│   ├── formatDate.ts
│   ├── cn.ts              # classnames utility
│   └── validators.ts
├── types/
│   ├── api.ts             # shared API response types
│   └── common.ts          # User, Pagination, etc.
└── constants/
    ├── routes.ts
    └── config.ts

Export from a single barrel:

// src/shared/index.ts
export { Button } from "./components/Button";
export { Modal } from "./components/Modal";
export { useDebounce } from "./hooks/useDebounce";
export { formatDate } from "./utils/formatDate";

Consumers import cleanly:

import { Button, Modal, useDebounce } from "@/shared";

Pages and Routes Layout

Separate layout components from page content. This keeps routing clean and layout reusable:

// src/layouts/MainLayout.tsx
import { Outlet, Link } from "react-router-dom";

export function MainLayout() {
  return (
    <div style={{ display: "flex", minHeight: "100vh" }}>
      <nav style={{ width: 250, background: "#f5f5f5", padding: "1rem" }}>
        <Link to="/dashboard">Dashboard</Link>
        <Link to="/settings">Settings</Link>
        <Link to="/profile">Profile</Link>
      </nav>
      <main style={{ flex: 1, padding: "2rem" }}>
        <Outlet />
      </main>
    </div>
  );
}
// src/App.tsx — route definitions stay thin
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { MainLayout } from "@/layouts/MainLayout";
import { AuthLayout } from "@/layouts/AuthLayout";
import { Dashboard } from "@/features/dashboard";
import { Settings } from "@/features/settings";
import { Login } from "@/features/auth";

const router = createBrowserRouter([
  {
    element: <MainLayout />,
    children: [
      { path: "/dashboard", element: <Dashboard /> },
      { path: "/settings", element: <Settings /> },
    ],
  },
  {
    element: <AuthLayout />,
    children: [
      { path: "/login", element: <Login /> },
    ],
  },
]);

export function App() {
  return <RouterProvider router={router} />;
}

Path Aliases

Relative imports like ../../../components/Button are fragile. Configure path aliases:

Vite (vite.config.ts):

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});

tsconfig.json (must match):

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

Now imports look like this:

import { Button } from "@/shared/components/Button";
import { useAuth } from "@/features/auth/AuthContext";
import { api } from "@/services/api";

No more counting ../../ segments. Move a file and the imports don't break.

Common Errors with Folder Structure

Error: 'Cannot find module' after moving a component

You moved a file but didn't update its imports. Use path aliases (@/) so you only update the import in the moved file, not every consumer. Run npx tsc --noEmit after every restructure.

Error: Circular dependency detected

Feature A imports from Feature B, and Feature B imports from Feature A. This happens when shared logic lives inside a feature. Extract the shared code into src/shared/ to break the cycle.

Error: 'React' is not in scope when using JSX

Rare with modern React 17+ and jsx: "react-jsx" in tsconfig. If you see this, you might be using an older setup or a file named .jsx instead of .tsx with an incompatible config.

Error: 'Module' has no exported member X

You imported from a barrel file (index.ts) that doesn't re-export the member. Check the barrel's exports. This is the most common mistake in feature-based structures.

Error: Path alias @/ not recognized

The tsconfig paths and bundler alias (vite.config.ts or webpack resolve.alias) must match exactly. Restart your editor and dev server after changing them

Practice Questions

  1. What is the main advantage of feature-based structure over type-based? Feature-based groups all files for one feature in one folder, making it easy to find, refactor, or delete an entire feature without touching unrelated code.

  2. What goes inside the shared/ directory? Reusable components, hooks, utilities, types, and constants that are used across multiple features — buttons, modals, debounce hooks, date formatters, API types.

  3. How do atomic design levels map to React? Atoms = single-purpose components (Button, Input), Molecules = combinations (SearchBar), Organisms = complex sections (Header), Templates = page layouts, Pages = route-level components.

  4. Why use barrel exports (index.ts)? Barrel files provide clean import paths (@/shared instead of @/shared/components/Button/Button), hide internal folder structure, and make refactoring easier.

  5. What causes circular dependencies in a feature-based structure? Feature A importing from Feature B while Feature B imports from Feature A. Fix by extracting the shared dependency into src/shared/.

Challenge

Design a folder structure for a project management app with these features: authentication (login, register, password reset), workspaces (list, create, settings), tasks (CRUD, drag-and-drop reorder, filters), notifications (in-app and email), and Team Chat. Create the folder tree (at least 30 files) using feature-based + atomic design. Then identify what belongs in shared/.

Real-World Task

Audit your current project's folder structure. List every file that is imported from three or more levels deep (e.g., ../../../../utils/helper). Refactor those imports to use path aliases. Then identify which features could be extracted into feature folders.

Mini Project: Scaffold Generator

Create a bash script that generates a new feature folder with the standard structure:

src/features/<feature-name>/
├── components/
├── hooks/
├── types/
├── services/
├── <FeatureName>.tsx
└── index.ts

The script should also add the barrel export to src/features/index.ts automatically.

Learning Path

flowchart LR
  A[React Hooks Complete Guide] --> B[React TypeScript]
  B --> C[React Folder Structure]
  C --> D[React State Management]
  D --> E[React Server Components]
  style C fill:#4a90d9,color:#fff

Next Steps

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. The feature-based structure described here mirrors how Durga Antivirus Pro organizes its quarantine, real-time scanning, and threat intelligence modules — each feature is self-contained, independently testable, and owned by a single team.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro