Skip to content

React Project Structure That Scales Without Becoming a Maze

Folder structure is a communication tool. A pragmatic layout for React apps that keeps pages, components, data and services easy to find as the project grows.

Muhammad Yasir

@YasirAwan4831

Published 2 min read

React Project Structure That Scales Without Becoming a Maze

On this page

Every React project starts tidy. A few months and thirty components later, nobody remembers where anything lives. A good structure is less about following a famous pattern and more about answering one question quickly: where does this code belong?

Organize by responsibility first

A simple, durable starting point separates code by what it does:

src/
  pages/        one file per route
  components/   reusable UI, grouped by feature
  data/         static content and mock data
  services/     data access (API, database)
  hooks/        shared custom hooks
  utils/        pure helper functions
  layouts/      page shells (header, footer)

Pages are thin

A page component should assemble things, not implement them. It fetches data, handles the loading and error states, and composes components. If a page grows past a couple of hundred lines, extract the parts into components named after what they show.

Keep data access behind a service

Components should not know whether articles come from a JSON file, a REST API or a database. Put all reads and writes in a small service module that returns plain objects. When the data source changes, only that module changes.

// services/contentRepository.js
export async function getPostBySlug(slug) {
  return posts.find((p) => p.slug === slug) ?? null;
}

Colocate what changes together

  • Group components by feature (blog/, projects/) rather than by type (buttons/, cards/).

  • Put truly generic pieces such as buttons, inputs and modals in components/ui/.

  • Keep a component, its small helpers and its tests near each other.

Rules of thumb

  1. Extract a component when you copy markup a second time, not before.

  2. Extract a hook when stateful logic is shared or makes a component hard to read.

  3. Keep pure functions (formatting, slugs, filtering) in utils/ so they are trivial to test.

  4. Prefer plain props and local state; add global state only when two distant parts genuinely need it.

Refactor on a schedule

Structure drifts. A short review every few weeks, asking what felt hard to find, keeps the project navigable without a big rewrite.

Enjoyed this article?

Share it with someone who might find it useful.