- Published on
Frontend Project Structure: Where the Hell Do I Put This File? (2025 Guide)
- Authors
- Name
- Asaf Shochet Avida
- @mrfrogrammer
How to Build a Frontend Project Structure That'll Make Your Team Sing
Introduction
Well-thought folder structure is not an end for itself, but one of the tools we have to enhance the project’s stability and team productivity.
Without it, your codebase will become a muddy swamp in no time. Bloop bloop.
When a new developer joins the team, the first question they often ask is, "Where should I put this function?" Whether you're working on a React project structure, Vue application, or Angular codebase, having clear, consistent folder organization helps answer that question right away.

Frontend Folder Structure That Works (and Why)
Your folder structure should help developers (and AI!) to:
- Find if existing functionality exists to avoid reinventing the wheel
- Know exactly where to put new code
- Ease thinking when adding features and debugging
- Quickly see if a specific area is tested by mirroring the folder structure in your tests
Here's an example structure that works well in React project structure, Vue project organization, Angular folder structure, and other modern JavaScript project setup:
src/
├── components/
├── pages/
├── services/
├── helpers/
├── hooks/
├── styles/
├── constants/
└── types/
For more complex or page-specific components - like a search bar used only on one page - create a subfolder inside components
or even within the page's folder to keep things tidy. This approach works particularly well for React project structure and Vue project organization.
Quick note on services vs helpers
Separating services (side-effect code) and helpers (pure functions) is key to writing testable frontend code. We’ll dive into this topic in a dedicated post soon.
Where Do Tests Go? JavaScript & TypeScript Testing Structure
Tests are just as important as code structure itself - but where should you put them?
There are two solid options, each with its pros and cons:
1. Parallel Test Folder Structure (My Preference)
Create a separate tests/
folder that mirrors your src/
structure:
src/
├── components/
│ └── Button.tsx
tests/
├── components/
│ └── Button.spec.ts
Pros:
- Keeps your
src
folder cleaner and focused on production code - Clear separation between source and tests
- Easy to see what’s tested and what’s not by comparing folder trees
- Keeps test files out of the production bundle
Cons:
- You have to keep the folder structures in sync
- More files/folders to navigate
2. Co-located Test Files (React/Vue Pattern)
Put test files right next to the files they test, usually named XXX.spec.ts
or XXX.test.ts
:
src/
├── components/
│ ├── Button.tsx
│ └── Button.spec.ts
Pros:
- Tests are close to the code they cover - easy to find and maintain
- No need to sync folder structures
- Easier to refactor since tests move with source files
Cons:
- Can clutter your source folders
- Tests may get bundled accidentally if not configured properly
Both options work, but personally, I prefer a separate folder to keep src
clean and focused.
For more insights on test performance and organization, check out The Hidden Impact of Test Performance and How to Improve Selenium Tests Performance.
File Naming Conventions That Work (React, Vue, Angular)
Here’s the deal: it doesn’t really matter what conventions you pick. What matters is that you pick some - and then actually stick to them.
Consistency saves time, reduces confusion, and keeps your team on the same page. Here are the conventions I see working well:
JavaScript & TypeScript File Naming Best Practices
- Pick kebab-case or camelCase for file and folder names - just pick one and don’t switch mid-project.
- Use PascalCase for React component filenames (
MyButton.tsx
) and TypeScript project structure files, so they stand out as UI pieces. - Avoid vague names like
stuff.ts
orhelpers2.ts
. Be descriptive but concise. - For SCSS files, use CapitalCase.scss matching component names, e.g.
Button.scss
,SearchBar.scss
. This naming convention works across React, Vue, and Angular folder structure setups.
Naming Helpers and Services
- Suffix utility files clearly:
- Use
xxxHelper.ts
for pure helper functions. - Use
xxxService.ts
for modules that handle side effects or external communication.
- Use
- This keeps responsibilities clear and makes testing easier.
utils
Stop Calling Everything The utils
or helpers
folder is a black hole where code goes to die.
Instead of dumping everything there, group helpers by purpose. And when you do find messy, unused utility code? Don't be afraid to delete it – as I discuss in Zen and the Art of Deleting Code, removing unnecessary code is often more valuable than adding new features.
src/
├── helpers/
│ ├── DateFormattingHelper.ts
│ ├── FileNamesHelper.ts
│ └── ResultsFilterHelper.ts
This makes it easier to find and maintain helpers.
Linter That Works
Linters aren’t just about catching bugs or enforcing style - they’re your first line of defense for keeping your project consistent and maintainable.
A few tips to get the most out of your linter:
- Start with a great preset. Skip reinventing the wheel. For JavaScript project setup, try
airbnb
. For TypeScript project structure, useplugin:@typescript-eslint/recommended
. Also considerplugin:cypress/recommended
orplugin:react-hooks/recommended
for React projects. - Enforce naming conventions. Use rules that flag file names, variable names, and folder names that don’t follow your agreed-upon style.
- No default exports. They make refactoring and tree shaking harder. Use named exports instead.
- Integrate linter into your build process. Make sure the build fails when lint errors occur - no exceptions.
The linter is your best friend when it comes to these nitty decisions. When a build fails because of a non-conventionally-fitting name, you won’t even have to waste time debating it in PRs.
Advanced: Enforce Folder Rules Using ESLint Plugins
As projects grow, just agreeing on folder structure isn’t enough - you want to enforce it automatically.
This is where ESLint plugins come in handy.
For example, eslint-plugin-boundaries
lets you define rules like:
- If you have one repo for frontend and backend - block cross imports to keep concerns separate.
- A service can import and use a helper, but a helper cannot import a service.
- Common services can be imported by components, but a service cannot import a component to avoid UI logic leaking into business logic.
Setting up rules like these means developers get instant feedback when they try to break conventions - no guesswork, no manual code reviews needed.
It’s like having your architecture guardrails coded right into your build process.
Here’s a minimal example ESLint config snippet focused on services and helpers:
// .eslintrc.js
module.exports = {
plugins: ['boundaries'],
rules: {
'boundaries/element-types': [
'error',
{
default: 'disallow',
rules: [
{
from: 'services',
allow: ['helpers'],
},
{
from: 'helpers',
allow: [],
},
],
},
],
},
settings: {
boundaries: [
{ type: 'services', pattern: 'src/services/**' },
{ type: 'helpers', pattern: 'src/helpers/**' },
],
},
}
Note: Adjust folder paths to your project.
Final Thoughts
A well-thought-out frontend folder structure isn't just about neatness - it's a key tool to boost your team's productivity, reduce bugs, and keep your project scalable. These frontend architecture patterns apply whether you're building with React, Vue, Angular, or vanilla JavaScript.
We covered practical folder organization, test placement strategies, naming conventions, and how to enforce rules automatically with linters.
In upcoming posts, we'll dive deeper into writing testable code by separating services and helpers, advanced testing strategies at scale, and more tooling tips to keep your frontend codebase clean and efficient.
Related articles you might find helpful:
- How to Become a Junior Frontend Developer with No Experience - Perfect if you're starting your frontend journey
- My Favorite Frontend Job Interview Question - How project structure knowledge helps in interviews