UI Folder Structure Patterns

Organizing a React application’s folder structure is crucial for maintainability, scalability, and collaboration.

Architecture-tied

Based on the vast amount of architecture patterns that exist, we can start combining concepts together to come up with different folder structure patterns, the most used ones in React applications could be:

Feature-Based Folder Structure

This pattern organizes files by feature or domain, grouping all related components, hooks, styles and logic together.

Folder Structure:

src/
├── features/
│   ├── auth/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   ├── slices/ (for Redux)
│   │   └── index.js
│   ├── dashboard/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   └── index.js
│   └── profile/
│       ├── components/
│       ├── hooks/
│       ├── services/
│       └── index.js
├── shared/
│   ├── components/ (reusable UI components)
│   ├── hooks/ (reusable hooks)
│   ├── utils/ (utility functions)
│   └── styles/ (global styles)
├── App.js
├── index.js
└── store.js (if using Redux)

When to Use:

  • Medium to large applications.

  • Teams working on different features independently.

  • Applications with clear domain boundaries.

Layered Architecture (Presentation, Business Logic, Data)

This pattern separates the application into layers based on their responsibilities: presentation, business logic, and data.

src/
├── presentation/
│   ├── components/ (UI components)
│   ├── pages/ (page-level components)
│   └── layouts/ (layout components)
├── business/
│   ├── hooks/ (custom hooks for business logic)
│   ├── services/ (API calls, business logic)
│   └── store/ (Redux or Zustand store)
├── data/
│   ├── api/ (API configuration and endpoints)
│   ├── models/ (data models or interfaces)
│   └── utils/ (data-related utilities)
├── App.js
├── index.js
└── routes.js (if using React Router)

When to use:

  • Applications with complex business logic

  • Teams that want clear separation of concerns

  • Applications that need to scale with a focus on maintainability

Atomic Design Pattern

This pattern organizes component based on their complexity and reusability, following the Atomic Design methodology.

src/
├── atoms/ (smallest components, e.g., buttons, inputs)
├── molecules/ (combinations of atoms, e.g., search bar)
├── organisms/ (combinations of molecules, e.g., header, footer)
├── templates/ (page layouts)
├── pages/ (page-level components)
├── hooks/ (custom hooks)
├── utils/ (utility functions)
├── styles/ (global styles)
├── App.js
├── index.js
└── routes.js (if using React Router)

When to Use:

  • Applications with a strong focus on reusable components

  • Design systems or component libraries

  • Teams that want to enforce a consistent design language

Modular Architecture

This pattern organizes the application into modules, where each module itself is a self-contained unit with its own components, styles, and logic.

src/
├── modules/
│   ├── auth/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── styles/
│   │   └── index.js
│   ├── dashboard/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── styles/
│   │   └── index.js
│   └── profile/
│       ├── components/
│       ├── hooks/
│       ├── styles/
│       └── index.js
├── shared/
│   ├── components/ (reusable UI components)
│   ├── hooks/ (reusable hooks)
│   ├── utils/ (utility functions)
│   └── styles/ (global styles)
├── App.js
├── index.js
└── routes.js (if using React Router)

Note: If you are familiar with Angular, the framework adopts this architecture pattern full stop.

When to Use:

  • Large applications with multiple independent modules

  • Teams working on different modules simultaneously

  • Applications that need to be easily extensible

Flat Structure

This pattern keeps the folder structure simple and flat, with minimal nesting

src/
├── components/ (all components)
├── hooks/ (custom hooks)
├── pages/ (page-level components)
├── services/ (API calls)
├── styles/ (global styles)
├── utils/ (utility functions)
├── App.js
├── index.js
└── routes.js (if using React Router)

When to Use:

  • Small applications or prototypes

  • Teams that prefer simplicity and minimal structure

  • Applications with limited complexity

Ducks Pattern (for Redux)

This pattern organizes Redux-related logic (actions, reducers, and types) into a single file per feature/module.

src/
├── features/
│   ├── auth/
│   │   ├── authSlice.js (Redux slice)
│   │   └── authActions.js (optional)
│   ├── dashboard/
│   │   ├── dashboardSlice.js
│   │   └── dashboardActions.js
│   └── profile/
│       ├── profileSlice.js
│       └── profileActions.js
├── shared/
│   ├── components/ (reusable UI components)
│   ├── hooks/ (reusable hooks)
│   ├── utils/ (utility functions)
│   └── styles/ (global styles)
├── App.js
├── index.js
└── store.js (Redux store configuration)

When to Use:

  • Applications using Redux for state management

  • Teams that want to colocate Redus logic with features

Domain-Driven Design (DDD)

This pattern recognizes the application based on domains or bounded contexts, aligning the folder structure with the business domain.

src/
├── domains/
│   ├── user/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   └── models/
│   ├── product/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   └── models/
│   └── order/
│       ├── components/
│       ├── hooks/
│       ├── services/
│       └── models/
├── shared/
│   ├── components/ (reusable UI components)
│   ├── hooks/ (reusable hooks)
│   ├── utils/ (utility functions)
│   └── styles/ (global styles)
├── App.js
├── index.js
└── routes.js (if using React Router)

When to Use:

  • Large applications with complex business domains

  • Teams that want to align the codebase with business requirements

Choosing the Right Pattern

  • Small Apps: Use a Flat Structure or Atomic Design

  • Medium Apps: Use Feature-Based or Modular Architecture

  • Large Apps: Use Layered Architecture, Domain-Driven Design, or Ducks Pattern (if using Redux)

The best pattern will depend on the team’s preference, the size of the application, and the complexity of the business logic.

KakeiBro’s Choice

When it comes to designing a folder structure that is based on an architectural pattern, we definitely have to look at it from multiple perspectives, whilst a checklist might be enough for some, it’s through experience and a feel for how the project would evolve and the type of people that it will require that we need to make as close of a good choice as we can. Nothing’s off the table technically, but this choice should be based on factual and time-aware data (meaning knowing about the current state of both the dev team and the tech stack).

One other concept that was used in order to make the decision was of Screaming Architecture. The pattern itself focuses on the idea that the code bases we build, extend and maintain should constantly be giving cues to the developers about the Domain of the software. We add extra buffers and overhead to the development process when trying to separate our units of code into technical concerns (BI, Repository, etc). Whereas the way to really understand and keep a mental map of the software, is by connecting all of the technical implementations with the actual problem the software solves. It is by doing this that we add more intuitive and extendable choices for a developer.

This obviously has to be architected with intent, and not just following a set of rules like dogma, every software is different, and tons of variables come into play when coming up with the solution, that not always is the best, but we have to get our foot off the starting line somehow. We should follow iterative principles and always aim at making the software better if an issue with a design choice manifests itself.

With all of that said, the architecture that was chosen for the UI project was Feature Based. Meaning that we will follow an idea like so:

src/
├── features/
│   ├── auth/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   ├── store/ (for Redux)
│   │   └── index.ts
│   ├── users/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   └── index.ts
│   ├── finances/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   └── index.ts
|   └── intelligence/
│       ├── components/
│       ├── hooks/
│       ├── services/
│       └── index.ts
├── shared/
│   ├── components/ (reusable UI components)
│   ├── hooks/ (reusable hooks)
│   ├── utils/ (utility functions)
│   └── styles/ (global styles)
├── App.tsx
└── index.ts

If you notice, the features have the same names as the modules that are present in our Domain Driven Design page.

We should always follow this design line, and stick to it, if a use case ever arises that we are not sure where it fits, a discussion on how to extend the initial spec should take place.