Table of Contents
- The App Router
- Explaining the strategy
- What is a feature
- Structuring routes
- Organizing common files
- Composing a feature
- Bringing it together
- Final recommendations
- Wrapping it up
The App Router
Recently, Next.js 13.4 was released, marking an important milestone: the new App Router is now production-ready. This feature provides much greater flexibility in terms of project structure. You are no longer required to have a strictly βpages-onlyβ directory where every JavaScript file inside of it is turned into a route, forcing you to store your other files elsewhere. Instead, you have a single βappβ folder where you can organize your pages alongside any other related files. This is called colocation, and it allows for more efficient development.
π Disclaimer: The recommendations in this article are based on the authorβs experience and opinion, and may not be the only or best way to structure a Next.js project.
Explaining the strategy
In this article, I will guide you through what I believe is the most flexible and scalable way to structure your Next.js project. While this article is tailored for the Next.jsβ App Router, many of the basics could apply to any React project.
Most project structures you may come across are role-driven, but in this guide, we will be taking a feature-driven approach. To understand what a feature is exactly, please read the next section.
What is a feature
A feature is a group of files that are related to each other and overall represent an area or topic of your project. As I will explain later on, creating features can improve the maintainability and navigation of your codebase.
π Some examples are: the authentication, the landing, the dashboard.
Dividing your project into multiple features should make you think about the Separation of Concern design principle.
There are no set of rules for creating a feature, but here are a few questions you can ask yourself before making one:
- Do I have at least two related files to start with?
- Would grouping them improve the maintainability of my codebase?
- Do these files share a common area or topic in my project?
- Do these files share a common responsibility in my project?
- Are all of the existing features in the project unable to do the job?
Note that a feature may or may not correspond to a route folder.
Remember, you are free to create features at any time. Actually, itβs often a good idea to first do the coding, free like a bird, and then think about optimizing. Because at that point, you have the big picture, and it becomes easier.
What benefits does a feature-driven structure bring? According to the Next Right Now documentation:
While MVC (and many other) design pattern groups code together based on βroleβ of each file, our approach is different and groups related code together.
MVC is very simple to understand because you donβt have to think about it, itβs very intuitive, at first. Therefore, itβs beginner-friendly.
But, it doesnβt scale.
When you reach a dozen different features, all that code is grouped together (e.g: βComponentsβ) even though itβs not related to each other. On the other hand, when a developer wants to do something, itβs often related to a βfeatureβ. But, the code related to the feature is splattered in many folders and sub-folders because of the MVC pattern.
This makes it much harder to locate the code, and doesnβt give an overview of all the related pieces.
Here is an example that demonstrates the aforementioned:
.
βββ components/
β βββ Some other component...
β βββ Some other component...
β βββ Some other component...
β βββ Some other component...
β βββ NewTweetForm.tsx
β βββ Some other component...
β βββ Some other component...
β βββ Some other component...
β βββ RecommendedTopics.tsx
β βββ Some other component...
β βββ Some other component...
β βββ Some other component...
β βββ Some other component...
β βββ Sidebar.tsx
β βββ Some other component...
β βββ Some other component...
β βββ Some other component...
β βββ TweetsList.tsx
βββ hooks/
β βββ Some other hook...
β βββ Some other hook...
β βββ Some other hook...
β βββ Some other hook...
β βββ Some other hook...
β βββ Some other hook...
β βββ Some other hook...
β βββ useNewTweet.ts
β βββ Some other hook...
β βββ Some other hook...
β βββ useRecommendedTopics.ts
β βββ Some other hook...
β βββ Some other hook...
β βββ Some other hook...
β βββ useTweets.ts
β βββ Some other hook...
β βββ Some other hook...
βββ pages/
βββ Some other page...
βββ Some other page...
βββ Some other page...
βββ Some other page...
βββ Some other page...
βββ Some other page...
βββ Some other page...
βββ Some other page...
βββ Home.tsx
βββ Some other page...
βββ Some other page...
βββ Some other page...
βββ Some other page...
βββ Some other page...
βββ Some other page...
.
βββ home/
β βββ Home.tsx
β βββ NewTweetForm.tsx
β βββ RecommendedTopics.tsx
β βββ Sidebar.tsx
β βββ TweetsList.tsx
β βββ useNewTweet.ts
β βββ useRecommendedTopics.ts
β βββ useTweets.ts
βββ profile/
β βββ ...
β βββ ...
β βββ ...
β βββ ...
βββ search/
β βββ ...
β βββ ...
β βββ ...
β βββ ...
βββ settings/
βββ ...
βββ ...
βββ ...
βββ ...
If you think about it, the difference lies in grouping your project based on logical reasoning, rather than mechanically categorizing every file, .
I believe this structure is more compatible with the way developers navigate a codebase. The downside, however, is that this approach can be more difficult for beginners, especially the first time they try it.
Taken a look at the comparison above, letβs say that you need to work on the home page. Which structure would make your life easier and point you in the right direction more quickly? I believe I made my point!
Structuring routes
The first thing we usually think about when structuring a web app are the routes and the pages related. Hereβs how the app router enables you to structure those:
.
βββ app/
βββ home
βββ landing
βββ page.tsx
Suppose you want to render either the landing page or the home page based on the authentication status. A good way to start is by writing the conditional rendering logic inside page.tsx
. Then, each case would have its own feature folder, allowing for neat grouping of everything related to it. Keep in mind that a folder is just a folder unless it contains a page.tsx
. Only then does it become a route.
What if you need to place other files related to the root, such as authentication? Letβs see it in action:
.
βββ app/
βββ home
βββ landing
βββ (root)/
βββ page.tsx
βββ useAuth.ts
If you need to store files which will only be used by the root, you may group them in the root folder. In this case, Iβm creating a route group by wrapping the folder name in parentheses. This is to avoid affecting the URL path.
Alternatively, if you need to store files common to the entire project, see the next section.
Organizing common files
It is common for projects to require global files, such as a basic button component. These common files are placed at the root level. In this specific case, and only in this case, a role-based grouping is adopted since these files are by definition not related to any feature. Here is an example of how it may look:
.
βββ app/
βββ ...
βββ components
βββ hooks
βββ models
βββ utilities
Please keep in mind that this is just an example, and you may group items in a way that makes sense to you.
There are no set rules for common files, but here are a few questions you can ask yourself before placing files in a common folder:
- Would this file be used generally by the entire project?
- Is this file not specific to any particular feature?
- Would the project benefit from global access to this file?
Composing a feature
Within a feature folder, you can include all relevant files for that feature, such as components, images, constants, hooks, etc. This level of colocation makes it easy to work on features, as everything is readily available. If you want the feature to have a corresponding route, you can also include a page.tsx
file.
If things become cluttered within a feature folder, you can further group files. Here is an example:
.
βββ app/
βββ ...
βββ (auth)/
βββ LogoutModal/
β βββ LogoutModal.tsx
β βββ LogoutModal.test.tsx
β βββ LogoutModal.stories.tsx
β βββ LogoutModal.module.css
βββ useAuth.ts
βββ User.ts
βββ sign-up/
β βββ page.tsx
βββ login/
βββ page.tsx
Letβs describe what we are looking at:
- First, you have the
auth
feature. It includes everything that concerns the authentication of this app. - The
sign-up
page. - The
login
page. - The
LogoutModal
component. Grouping it in its own folder enables you to keep everything related to it in the same place. - The
User
model. - The
useAuth
hook.
Bringing it together
We have covered a few topics in a step-by-step manner. Now, itβs time to bring everything together:
.
βββ app/
βββ components/
β βββ Button/
β βββ Button.tsx
βββ home/
β βββ TweetsList/
β β βββ TweetsList.tsx
β βββ Sidebar/
β β βββ Sidebar.tsx
β βββ page.tsx
β βββ useTweets.ts
βββ (auth)/
β βββ useAuth.ts
β βββ User.ts
β βββ sign-up/
β β βββ page.tsx
β βββ login/
β βββ page.tsx
βββ hooks/
β βββ useSomething.ts
βββ utils/
βββ makeThings.ts
Letβs write a summary of the above:
- The global folders:
components
,hooks
, andutils
. - The feature folders:
home
andauth
. - The pages:
/home
,/sign-up
, and/login
.
Final recommendations
- I suggest wrapping each component in a folder from the beginning. You will likely need additional files such as a style or test, so itβs best to prepare for them.
- To reduce complexity, I recommend to avoid deep nesting. Typically, a one level nesting starting from the route or sub-route folder is sufficient.
- If you have difficulty identifying models, you may name them something like
User.model.ts
. - Remember that you can make use of route groups that do not affect the URL paths.
Wrapping it up
I hope this article was helpful and inspiring for you to structure your project in a way that is scalable yet fits your needs. If I missed anything or if you have any doubts, please leave a comment and I will be happy to get back to you!
If you are interested in learning more about Next.js 13.4, you can check it out here. Also, you can find the documentation to the App Router here.
Thanks for reading.