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!
Top comments (12)
Sorry if this is a silly question but why is
elements
nested incomponents
? Are there supposed to be sibling folders toelements
? If so, what?With atomic design you organize your components in five different types; atoms, molecules, organisms, templates, pages. Maybe the OP is insinuating something similar with his 'elements' folder. For example, there could be an 'elements' and a 'views' folder in there, or something.
See: bradfrost.com/blog/post/atomic-web...
I have the exact same question, not sure why you need that
I am wondering the same thing
Thanks for taking the time to do a write up, we're all looking for good places to "put our stuff"!
From your folder structure however, I cannot deduce whether /modules/auth/api/AuthAPI.js would be something imported in /modules/auth/components/AuthForm.tsx (as a helper that exposes the API to the front-end components) or /pages/api/authAPI/authAPI.js (as a utility file that the page Javascript module defers to for actual authing) or somewhere else. In other words, is it front- or backend code?
The use of the term "module" is also tricky territory in a Javascript context, since it is often referred to when talking about a single file. (I had to call it "Javascript module" above for example! :)
Hi, thanks!
I'm a little confuddled about nextjs and its API implementation. I'm coming from experience with express routing, in which I would bind endpoints in a routes controller.
What sorts of functions go in your /pages/api/auth/authAPI? and which go in your /modules/auth/api/authAPI? and the /auth/auth.js?
This was awesome, thank you.
Do you have an example codebase using this structure, to see it a little more concrete?
I'm coming from express routing for apis, and so I'm a little confuddled by how I should organize things. If I have a folder structure like
/modules
/auth
/api
AuthAPI.js
/components
auth.js
/pages
/api
/authAPI
authAPI.js
What kind of functions go in /modules/auth/api/AuthAPI? /modules/auth/auth.js?
What kind of functions go in /pages/api/authAPI/authAPI.js?
Also, is [name]API.js your shorthand for all the other ones, or is that your "router" ?
I've adopted the same architecture because it allows me to separate use case artifacts from everything else.
Well done mate.
How about each module in its own NPM Package ?
youtube.com/watch?v=p3vIeN1_o1I has a demo to split NextJs Project into separate modules
I'm gonna use this approach with in my current project.
Where do you put your queries when using graphql? src/common/queries or rather src/modules/auth/api/queries ?