React Folder Structure — Best Practices for Scalable Projects
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
Practice Questions
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.
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.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.
Why use barrel exports (
index.ts)? Barrel files provide clean import paths (@/sharedinstead of@/shared/components/Button/Button), hide internal folder structure, and make refactoring easier.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
- Previous: React TypeScript — Using TypeScript with React, React Hooks Complete Guide
- Next: React State Management — Redux, Zustand, Context API Compared, React Server Components — Next.js App Router Guide
- Related: TypeScript Cheatsheet, Next.js Cheatsheet
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