I’ve been using React for over 10 years, and as some of the projects I’ve attempted have grown, I’ve made a few changes to my React file structure this year:
- Filenames are kebab-case (from PascalCase)
- Folder names are always lowercase (kebab-case)
- No more
index.jsbarrel files - Named exports > default exports
- No more webpack aliases
@components - Added a
featuresfolder for bigger projects
And here are my reasons:
Filenames are kebab-case (from PascalCase)
In React, it’s common to see component filenames in PascalCase. It’s even like this in the React docs. And that’s how I have always named React component files: ComponentName.js.
My components files used to look like this:
📁 api/
📁 components/
└── 📁 ComponentName/
├── 📄 ComponentName.js
├── 📄 ComponentName.helpers.js
├── 📄 useThisHook.js
└── 📄 index.js
└── 📁 List/
├── 📄 List.js
├── 📄 ListItem.js
└── 📄 index.js
📁 hooks/
├── 📄 useHookOne.js
└── 📄 useHookTwo.js
📁 utils/
├── 📄 auth.js
├── 📄 teams.js
└── 📄 users.js
Using PascalCase for React component files seems fairly common. However, one thing I dislike about it is that the files use different naming conventions. Check out ComponentName.
- if it’s a component, it's PascalCase.
- if it’s a component helper, it's PascalCase.
- if it’s a hook, it’s camelCase.
- if it’s a util (or if a component helper moves to a util), it's kebab-case.
I find making decisions really taxing. It drains me. I prefer to make decisions once and then move on. The problem with the way I was naming files before (like in the example above) was that I had to at least give some thought to how to name the file.
Why should helper files in the helper directory start with a lower case letter (kebab-case), but helper files in a component directory start with an upper case letter (PascalCase). It’s not a component, but it has an uppercase at the start of the filename because it’s associated with that component, I guess. But then if that helper ends up being used by more than one component, and I need to move it to utils, I also need to rename it.
For me, it doesn’t look or feel very uniform. Especially on bigger projects, and monorepos where I have server files for the api code that are lowercase, and app files for the React code where some are uppercase, and some are lowercase.
And sometimes file systems can order uppercase and lowercase differently. On some systems, the uppercase will come after the lowercase, and on some, the order is case insensitive. This can mean that the file order is inconsistent if you are working in multiple environments.
And yes, React requires that the first letter of components be capitalized, but that doesn’t mean the file name also has to follow this pattern. So, going forward, I’m preferring to always use kebab-case for filenames.
No more PascalCase for React component files, and camelCase for hooks files. Just kebab-case for everything.
Here’s how my file structure looks now:
📁 api/
📁 components/
└── 📁 component-name/
├── 📄 component-name.js
├── 📄 component-name.helpers.js
├── 📄 use-this-hook.js
└── 📄 index.js
└── 📁 list/
├── 📄 list.js
├── 📄 list-item.js
└── 📄 index.js
📁 hooks/
├── 📄 use-hook-one.js
└── 📄 use-hook-two.js
📁 utils/
├── 📄 auth.js
├── 📄 teams.js
└── 📄 users.js
Folder names are always lowercase (kebab-case)
You may have noticed from my old file structure that components often end up in folders. Maybe because the component has subcomponents, hooks, or helper functions, and they are large or complex, so I want to separate them into different files.
And I originally used PascalCase for the component folders, just like the component files. Now the files are kebab-case, you’ll notice the folders are too! This means that all my folders are lowercase, just like my files.
I’m much happier since making this decision, because my folders look more uniform.
No more index.js barrel files
Every component used to have an extra index.js barrel file.
📁 components/
└── 📁 component-name/
├── 📄 component-name.js
├── 📄 component-name.helpers.js
├── 📄 use-this-hook.js
└── 📄 index.js
└── 📁 list/
├── 📄 list.js
├── 📄 list-item.js
└── 📄 index.js
This index.js file simply exports the component:
export * from './list';
export { default } from './list';This is a little trick to simplify imports. Now, instead of importing the file, you can import the directory.
// with index.js file
import List from '../components/list';
// without index.js file
import List from '../components/list/list';But I have got rid of my index.js barrel files:
📁 components/
└── 📁 component-name/
├── 📄 component-name.js
├── 📄 component-name.helpers.js
└── 📄 use-this-hook.js
└── 📁 list/
├── 📄 list.js
└── 📄 list-item.js
Ok, some people got rid of index.js barrel files because of performance, tree-shaking, etc. I’m not going to pretend I’ve looked into all that. I didn’t notice any problems with performance in my fairly large React apps.
I was just a bit tired of creating these index.js files and feeling like they didn’t do a lot.
So now instead of importing like import List from '../components/list';, I need to reference the file and not just the folder, like import List from '../components/list/list';.
But my code editor mostly automates this, and even if it didn’t, it’s a tiny bit of extra typing, instead of having to create an extra index.js file for every component I create. I’m happy with the trade-off.
Named exports > default exports
I've started preferring named exports over default exports in my React code.
This one's a bit of a new development for me. I used to export my React components as the default export - like:
import React from 'react';
function Button(props) {
//...
};
export default Button;And then import them like:
import Button from '../components/button/button';But I was running into a couple of issues. Sometimes, I would export multiple components from one file, like Button, PrimaryButton, and SecondaryButton.
With a mixture of default and named exports, I would import them this way:
import Button, { PrimaryButton, SecondaryButton } from '../components/button/button';Nothing too major, but I’d need to go check in the file sometimes what the default export was.
And when you use a default export, you can name it whatever you like. I tried to be consistent, and mostly was, but sometimes I would name the import something different, and when I was searching around for where I was using a component, I’d miss some files.
Now I always use named exports for my components (apart from lazy-loaded components which need to use default exports to work, but these are now in the new features directory we will discuss shortly!).
import React from 'react';
function Button(props) {
//...
};
export { Button };And with named exports, my imports must always use the right name, or I’ll get an error.
import { Button } from '../components/button/button';No more webpack aliases @components
I really like webpack aliases. It was pretty cool importing @components and @hooks, and not having to worry about how many directories I need to traverse to get to the real location of those files.
// with aliases
import { Button } from `@components/button/button`;
// without aliases
import { Button } from '../components/button/button';
import { Button } from '../../components/button/button';But features that don’t use Webpack, like VS Code intellisense for autocompleting imports etc, don’t understand it. So then I need to create a jsconfig.json file. And testing libraries also need to know where those imports are.
And once I started having to configure these aliases in multiple places, I figured if I need to make changes in the future, it’s going to be whole thing trying to remember all the places I set the aliases up. So rather than webpack aliases, I just try to keep my file structure fairly flat, so I never have to traverse too far (../../../../../!).
Added a features folder for bigger projects
On big projects, my components folder would get pretty big. And to be honest, some of the components would get pretty big with subcomponents, because they weren’t reusable components, but more a feature of the application. Like a sign-in page or account settings.
After reading a blog post about React Folder Structure by Robin Wieruch, I realised I needed exactly that, a features folder.
📁 api/
📁 components/
└── 📁 component-name/
├── 📄 component-name.js
├── 📄 component-name.helpers.js
└── 📄 use-this-hook.js
└── 📁 list/
├── 📄 list.js
└── 📄 list-item.js
📁 features/
└── 📁 auth/
├── 📁 login
├── 📁 register
├── 📁 logout
├── 📄 auth.helpers.js
└── 📄 auth.routes.js
└── 📁 settings/
📁 hooks/
├── 📄 use-hook-one.js
└── 📄 use-hook-two.js
📁 utils/
├── 📄 auth.js
├── 📄 teams.js
└── 📄 users.js
Now with a quick glance in my features folder, I can get a good idea of the features of the app I am working. And I know where the feature-specific components are (📁 features/), and where the shared components are (📁 components/).
The best and worst thing is that you can structure your React projects however you like. But if you are like me and struggle with such massive decisions, here are a couple of other posts I have taken inspiration from in the past, before tweaking to my own preference: