A 2021 guide about structuring your Next.js project in a flexible and efficient way
Vadorequest
Posted on January 21, 2021
Next.js
Next.js is very unopinionated about how to structure your Next.js project.
The only thing you must really be careful about is to not have anything under
pages
that aren't actual pages (e.g: tests, components, etc.), because there is no way to ignore them and Next will bundle and deploy them as actual pages.
You can choose to have your code at the root level (/pages
) or under src
(/src/pages
). We prefer the latter, because it makes it more obvious what is actual app-related code and what isn't.
The usual folder structure
Most people will organize their project using a folder structure such as:
/public
favicon.ico
/src
/components
/elements
/auth
AuthForm.tsx
AuthForm.test.ts
/[Name]
[Name].tsx
[Name].test.ts
/hooks
/types
/utils
/test
/api
authAPI.test.js
[name]API.test.js
/pages
index.test.js
/pages
/api
/authAPI
authAPI.js
/[name]API
[name]API.js
_app.tsx
_document.tsx
index.tsx
This design pattern is very common, and it's great for small project because it makes it obvious where your files should be located. They're grouped by "kind of files". It's very similar to the MVC design pattern many developers are familiar with.
The main issue with this design pattern is its lack of scalability.
While it's great at the beginning, and can be a good fit depending on the size of your project, you'll realize a some point you'd find your files faster if they were grouped together by "module".
Once you reach 6-10 unrelated features, you'll wish to have the components files close to the utilities and to the TS type that are specific to your UI components, or even maybe your data model.
Also, you might prefer having all files related to a particular 3rd party within the same folder, as a module. (e.g: Sentry, Amplitude, etc.)
At some point, splitting files based on their kind will not be good enough. That's where you'll need modules.
The flexible and "modular" folder structure
Another way to organize things is to introduce modules.
Modules help group together code that is related to each other. They're not a replacement for what's common.
Here is how we might convert our previous folder structure to something a bit more modular:
/public
favicon.ico
/src
/common
/components
/elements
/[Name]
[Name].tsx
[Name].test.ts
/hooks
/types
/utils
/modules
/auth
/api
AuthAPI.js
AuthAPI.test.js
/components
AuthForm.tsx
AuthForm.test.ts
auth.js
/pages
/api
/authAPI
authAPI.js
/[Name]API
[Name]API.js
_app.tsx
_document.tsx
index.tsx
We added a new src/modules/
folder where we group all files related to the same feature (here, the authentication as "auth"), instead of splitting the "auth" code everywhere, it is now centralized into a single folder.
You might have noticed the only thing that hasn't changed is the src/pages/
directory. I'll repeat myself, but you must not have anything there that isn't either a page or an API endpoint.
Everything related to the authentication is now in /src/modules/auth
, it's much easier/faster to understand the code being used to authenticate now!
But, you don't want to always use modules, right? Sometimes you're writing some kind of utility that doesn't really fit in any module, something you'll want to write some code quickly and be done with it.
Modules introduce "thoughts complexity", because now you have a conscious choice to make about where your file should be. It was easier before to make this decision, because grouping files by kind is effortless, it's a choice that has an actual answer to it. Moving a component to the hooks
folder is wrong, while moving it to the components
is correct.
It's easy to know you did it right. (or wrong)
But, with modules, there is no right/wrong answer! Making it harder to make decisions. Sometimes you won't know, it won't be obvious at first (it may never be). Sometimes you'll figure it out afterwards ("oh, that's actually a module").
And because modules aren't the universal solution to this problem, the key is to allow both.
The common
directory should be used for everything that isn't a module, while the modules
directory should be used by everything you feel should be a "module".
This way, you get the best of both worlds:
- The ability to quickly add code without thinking much about where it should be (
common
). - The ability to organize at your own pace when you feel like that thing has grown too big and all those pieces of code should be brought together (converting from
common
tomodules
). - The ability to quickly find your code in your existing modules and to have an overview of how big a module is.
I'm the author of Next Right Now, a Next.js production-ready boilerplate, and the above folder structure is what we're using since January 20, 2021. It is the result of feedbacks from the NRN community.
Alongside this new folder structure, Next Right Now has also migrated to "Module path aliases", which uses absolute paths instead of relative paths for importing modules. (e.g: import ('@/common/hooks/useStuff')
instead of import ('../../../common/hooks/useStuff')
.
If you want to learn more on the topic, read the NRN folder structure documentation!
Posted on January 21, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
January 21, 2021