DEV Community

Vadorequest
Vadorequest

Posted on

A 2021 guide about structuring your Next.js project in a flexible and efficient way

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 to modules).
  • 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)

Collapse
 
swiftwinds profile image
SwiftWinds

Sorry if this is a silly question but why is elements nested in components? Are there supposed to be sibling folders to elements? If so, what?

Collapse
 
kimskogsmo profile image
Kim Skogsmo • Edited

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...

Collapse
 
oseisaac profile image
ISaac

I have the exact same question, not sure why you need that

Collapse
 
andrej_gajdos profile image
Andrej Gajdos

I am wondering the same thing

Collapse
 
alexman profile image
Alexander Hofstede

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! :)

Collapse
 
mtrogers profile image
mtrogers

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?

Collapse
 
mtrogers profile image
mtrogers

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?

Collapse
 
mtrogers profile image
mtrogers

Also, is [name]API.js your shorthand for all the other ones, or is that your "router" ?

Collapse
 
flowck profile image
Firmino Changani

I've adopted the same architecture because it allows me to separate use case artifacts from everything else.

Well done mate.

Collapse
 
raaghu profile image
Raghavendra

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

Collapse
 
onkeltem profile image
Artiom Neganov

I'm gonna use this approach with in my current project.

Collapse
 
gutisalex profile image
gutisalex

Where do you put your queries when using graphql? src/common/queries or rather src/modules/auth/api/queries ?