Organizing by file type (components/, hooks/, utils/) works well for small apps, but scatters related code across folders as an app grows. Feature-based architecture groups code by domain instead. This lesson covers feature folders, a shared/ folder for cross-cutting code, and the cross-feature import discipline that makes it work.
1When Organizing by File Type Breaks Down
A classic type-based layout ā components/, hooks/, utils/, pages/ ā works well for small apps, but as an app grows past a few dozen files, working on any single feature means jumping between several different top-level folders just to see everything related to it.
2Grouping by Feature Instead
Feature-based architecture inverts the organizing principle: each feature gets its own folder containing its components, hooks, and utilities together, colocated by what they're for rather than what kind of file they are.
3A shared/ Folder for Truly Reusable Code
Code genuinely used across multiple features ā a Button component, a formatCurrency utility ā doesn't belong to any single feature folder. A top-level shared/ (or common/) folder is reserved specifically for this kind of cross-feature code.
4The Import Rule: Features Don't Reach Into Each Other
The core discipline of feature-based architecture is a rule that one feature folder should never directly import another feature folder's internals. Code genuinely needed by multiple features should be promoted to shared/, keeping features independent and preventing tangled cross-feature dependencies.
5When to Actually Adopt This
Feature-based architecture pays off once an app has enough distinct domains that type-based folders start feeling cluttered, typically past a few dozen components. For smaller or early-stage projects, the simpler type-based structure remains a reasonable, lower-overhead default.
6Step-by-Step Breakdown
When Organizing by File Type Breaks Down. You already know the classic layout: components/, hooks/, utils/, pages/. It works great for small apps ā but as an app grows past a few dozen files, working on 'checkout' means jumping between five different top-level folders just to see everything related to that one feature.
Grouping by Feature Instead. Feature-based architecture flips the organizing principle: instead of components/, hooks/, utils/ at the top level, each FEATURE gets its own folder containing everything it needs ā its components, hooks, and utilities live together, colocated by what they're for, not what they are.
What's the core organizing principle behind feature-based architecture, compared to organizing by file type?
- āFiles are grouped by what feature/domain they belong to, not what kind of file they are
- āFiles are grouped by their file size in kilobytes
A shared/ Folder for Truly Reusable Code. Not everything belongs to one feature ā a Button component or a formatCurrency utility might be used across checkout, profile, and dashboard alike. These live in a top-level shared/ (or common/) folder, reserved specifically for code with no single feature owner.
The Import Rule: Features Don't Reach Into Each Other. The real discipline behind this architecture is a rule: one feature folder should never directly import from another feature folder's internals. If checkout needs something from profile, that code should be promoted to shared/ ā this keeps features independently deletable and prevents a tangled web of cross-feature dependencies.
Why is it discouraged for the checkout feature folder to directly import a hook from inside the profile feature folder?
- āIt keeps features independent and prevents a tangled web of cross-feature dependencies
- āCross-feature imports are simply slower at runtime
When to Actually Adopt This. Feature-based architecture pays off once an app has enough distinct domains that type-based folders start feeling cluttered ā usually somewhere past a few dozen components. For a small app or an early-stage project, the simpler type-based structure from the Folder Structure lesson is often still the better, lower-overhead choice.
Mastery Achieved. You now understand feature-based architecture: grouping code by domain instead of file type, a shared/ folder for genuinely cross-feature code, the discipline of never reaching directly into another feature's internals, and recognizing when this shift is actually worth making. Next, you'll go deeper into how individual components themselves should be structured.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
This is a project organization convention, not a runtime browser feature.
Fully applicable.
Fully applicable.
Fully applicable.
Accessibility (A11y)
1Consistent Feature Structure Simplifies Accessibility Audits
When every feature folder follows the same internal layout, it's easier for an accessibility review to systematically walk through each domain, rather than hunting across scattered type-based folders for all the pieces of one feature.
SEO Implications
- 1
Feature-Based Organization Has No Direct SEO Effect
This is purely a codebase organization convention affecting developer experience and maintainability, with no bearing on runtime output or server-rendered content.
Best Practices
Start Type-Based, Migrate to Feature-Based When It Earns Its Keep
Don't impose feature-based architecture on a brand-new, small project preemptively ā introduce it once the type-based structure genuinely starts causing friction, typically as the number of distinct domains grows.
Enforce the No-Cross-Feature-Import Rule with Linting Where Possible
Tools like eslint-plugin-boundaries can programmatically enforce that feature folders don't import each other's internals, catching violations automatically instead of relying purely on code review discipline.
Frequent Bugs
Deleting a feature folder breaks unrelated parts of the app.
This usually means another feature was directly importing from the deleted feature's internals, violating the no-cross-feature-import rule. Promote genuinely shared code to a shared/ folder before this happens, and avoid direct feature-to-feature imports going forward.
It's unclear whether a new utility function belongs in a specific feature folder or the shared/ folder.
A reasonable rule of thumb: if it's used by only one feature today, keep it in that feature's folder ā it can always be promoted to shared/ later if a second feature genuinely needs it, avoiding premature shared/ folder clutter.
Real-World Examples
Migrating an E-Commerce App to Feature-Based Architecture
An e-commerce app's type-based structure had grown to include dozens of files each in components/, hooks/, and utils/, making it hard to find everything related to checkout or product browsing. Reorganizing into features/checkout/, features/products/, and features/cart/, with a shared/ folder for the Button, Modal, and formatCurrency utilities used everywhere, made each domain's code immediately discoverable in one place.
src/
features/
checkout/
CheckoutForm.tsx
useCheckout.ts
products/
ProductList.tsx
useProducts.ts
shared/
components/Button.tsx
utils/formatCurrency.ts