Home
Daily
How I Organize React Projects
November 02, 2025
1 min

Table Of Contents

01
1. Organize by Feature, Not by Type
02
2. Create a shared/ Folder for Reusable Code
03
3. Naming Conventions Matter
04
4. Think in Layers
05
5. A Scalable Project Structure Example

Here are five lessons that completely changed how I structure React projects

1. Organize by Feature, Not by Type

A common mistake many developers make (including my past self) is splitting everything by type:

src/
components/
hooks/
pages/
utils/

That seems neat at first — until your app grows. Suddenly, you’re jumping across files and folders just to make one change.

A better way: group by feature. Keep everything related to a feature together — components, hooks, and API calls included.

src/
features/
auth/
components/
hooks/
api/
dashboard/
components/
hooks/
api/

2. Create a shared/ Folder for Reusable Code

Some elements — buttons, modals, utility functions — are used everywhere. Put these in a shared/ folder to avoid duplication and keep your code DRY (Don’t Repeat Yourself).

src/
shared/
components/
hooks/
utils/

This helps maintain consistency across your app and reduces future headaches.

3. Naming Conventions Matter

Clear naming = clear thinking. Here’s what works well:

  • Components → PascalCase → UserProfile.jsx
  • Hooks & Utils → camelCase → useAuth.js, formatDate.js
  • index.js → use only for re-exports

Small details like this make your project easier to navigate — especially for new contributors.

4. Think in Layers

Just like in backend development, frontend apps have layers too. Understanding them helps you keep your architecture clean and modular.

  • UI Layer → Components
  • State Layer → Context, Redux, or Zustand
  • Data Layer → API calls (Axios, fetch, React Query)
  • Utilities Layer → Helper functions

👉 Rule: Keep these layers decoupled.

5. A Scalable Project Structure Example

my-app/
├── public/
│ └── favicon.svg
├── src/
│ │
│ ├── app/ # Application bootstrap & global config
│ │ ├── App.tsx
│ │ ├── main.tsx
│ │ │
│ │ ├── layouts/
│ │ │ ├── MainLayout.tsx
│ │ │ ├── AuthLayout.tsx
│ │ │ └── DashboardLayout.tsx
│ │ │
│ │ ├── providers/ # Global providers
│ │ │ ├── QueryProvider.tsx
│ │ │ ├── StoreProvider.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ └── RouterProvider.tsx
│ │ │
│ │ ├── routes/ # Centralized routing
│ │ │ ├── index.tsx
│ │ │ ├── ProtectedRoute.tsx
│ │ │ └── routeConfig.ts
│ │ │
│ │ └── store/ # Global state config
│ │ ├── index.ts
│ │ ├── rootReducer.ts
│ │ └── middleware.ts
│ │
│ ├── shared/ # Truly reusable across features
│ │ ├── components/ # Design system components
│ │ │ ├── Button/
│ │ │ │ ├── Button.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Input/
│ │ │ ├── Table/
│ │ │ └── Modal/
│ │ │
│ │ ├── hooks/ # Generic hooks
│ │ │ ├── useDebounce.ts
│ │ │ ├── useToggle.ts
│ │ │ └── useIntersection.ts
│ │ │
│ │ ├── utils/ # Pure functions (NO React)
│ │ │ ├── date.ts
│ │ │ ├── validation.ts
│ │ │ └── formatCurrency.ts
│ │ │
│ │ ├── types/
│ │ │ └── common.ts
│ │ │
│ │ ├── constants/
│ │ │ └── index.ts
│ │ │
│ │ └── styles/ # Global styling
│ │ ├── globals.css
│ │ └── variables.css
│ │
│ ├── features/ # 🔥 Business features (CORE of system)
│ │
│ │ ├── auth/
│ │ │ ├── api/
│ │ │ │ └── authApi.ts
│ │ │ │
│ │ │ ├── model/
│ │ │ │ ├── authSlice.ts
│ │ │ │ ├── authSelectors.ts
│ │ │ │ └── authTypes.ts
│ │ │ │
│ │ │ ├── ui/
│ │ │ │ ├── LoginForm.tsx
│ │ │ │ └── AuthLayout.tsx
│ │ │ │
│ │ │ ├── hooks/
│ │ │ │ └── useAuth.ts
│ │ │ │
│ │ │ └── index.ts
│ │ │
│ │ ├── users/
│ │ │ ├── api/
│ │ │ ├── model/
│ │ │ ├── ui/
│ │ │ ├── hooks/
│ │ │ └── index.ts
│ │ │
│ │ └── dashboard/
│ │ ├── api/
│ │ ├── model/
│ │ ├── ui/
│ │ └── index.ts
│ │
│ ├── entities/ # Domain entities (optional advanced layer)
│ │ ├── user/
│ │ │ ├── types.ts
│ │ │ └── mapper.ts
│ │ └── product/
│ │
│ ├── services/ # Infrastructure layer
│ │ ├── http/
│ │ │ ├── httpClient.ts
│ │ │ ├── interceptors.ts
│ │ │ └── errorHandler.ts
│ │ │
│ │ ├── storage/
│ │ │ └── localStorage.ts
│ │ │
│ │ └── logger/
│ │ └── logger.ts
│ │
│ ├── lib/ # Third-party wrappers
│ │ ├── react-query.ts
│ │ ├── dayjs.ts
│ │ └── sentry.ts
│ │
│ ├── config/ # Runtime configs
│ │ ├── env.ts
│ │ ├── featureFlags.ts
│ │ └── permissions.ts
│ │
│ ├── tests/ # Global test utilities
│ │ ├── setupTests.ts
│ │ ├── renderWithProviders.tsx
│ │ └── mockServer.ts
│ │
│ ├── e2e/ # Playwright / Cypress tests
│ │ ├── auth.spec.ts
│ │ └── dashboard.spec.ts
│ │
│ └── index.d.ts # Global type declarations
├── .env
├── .env.development
├── .env.production
├── package.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
├── vitest.config.ts
├── playwright.config.ts
├── .eslintrc.cjs
├── .prettierrc
├── .editorconfig
├── .lintstagedrc
├── .gitignore
└── README.md

Simple, predictable, and scalable.

Example Aliases (Vite or Webpack)

// vite.config.js
resolve: {
alias: {
'@app': '/src/app',
'@features': '/src/features',
'@shared': '/src/shared',
'@assets': '/src/assets',
}
}

Tags

#redux

Share

Related Posts

Daily
clsx
February 02, 2026
1 min
© 2026, All Rights Reserved.
Powered By

Social Media

githublinkedinyoutube