Skip to the content.

Folder structure should follow architecture, not file type

Folder structure should follow architecture, not file type

The instinct to group files by type — all components in /components, all styles in /styles, all tests in /tests — feels tidy and ages badly. It optimises for a question nobody asks (“show me every component”) and pessimises the one everyone asks (“show me everything about the checkout feature”), which is now scattered across four folders. A folder structure that instead mirrors your architecture — atoms, molecules, organisms, containers, state — turns the directory tree into documentation: a new reader can infer how the app is built just by expanding folders. Structure is a message to the next person, and type-grouping sends the wrong one.

Group by type scatters a feature; group by architecture colocates it Left: folders by type (components, styles, tests) with one feature's files scattered across all three. Right: folders by architecture layer (ui/atoms, containers, state) with related files together. by type components/ … Cart.jsxstyles/ … Cart.csstests/ … Cart.test.js one feature, three folders by architecture ui/atoms/Button.jsxcontainers/CartContainer.jsxstate/cart.js layers legible at a glance
By type, one feature's files are spread across three folders. By architecture, the tree itself shows the layers — atoms, containers, state — and where each thing belongs.

By-type folders hide the architecture

When files are grouped by extension, the directory tree tells you the language, not the design. You cannot see from /components which components are atoms and which are feature organisms, which are pure and which fetch — the very distinctions that matter are invisible:

src/
  components/   Button, Cart, UserCard, Header, ProductGrid…  (atoms? organisms? who fetches?)
  styles/       Button.css, Cart.css, …
  tests/        Button.test.js, …
# to understand Cart, open three folders; to know if Cart may fetch, open the file

By-architecture folders are documentation

Group by the architecture instead and the tree answers design questions on sight. The layer a file lives in tells you its rules — an ui/atoms/ file is pure and takes props; a containers/ file is where data enters; state/ holds reducers and selectors:

src/
  ui/
    atoms/       Button.jsx      (pure, props only — the folder implies the rule)
    molecules/   FormField.jsx
    organisms/   ProductGrid.jsx (still no fetch — it's under ui/)
  containers/    CartContainer.jsx (the only place allowed to touch the store)
  state/         cart.js, selectors.js
  lib/           formatMoney.js  (pure helpers)

A new engineer reads this tree and knows, before opening a file, that an organism does not fetch and a container does — because the structure is the architecture.

Colocate what changes together

The deeper principle is colocation: things that change together should live together. A component’s markup, its styles, its stories, and its test are one unit of change, so they belong in one folder, not scattered by type across the tree:

ui/molecules/FormField/
  FormField.jsx
  FormField.style.css
  FormField.stories.jsx
  FormField.test.jsx      # everything about FormField, in one place

Now editing FormField is opening one folder, and the by-type sprawl is gone. Structure by architecture and colocate by feature and your tree becomes a map a newcomer can navigate without a guide — and the map stays honest, because the folder a file sits in declares the rules it must follow. The atom-boundaries and presentational-vs-container exercises are exactly about placing a component on this map and honouring the rule its location implies.