The main file structure is organized as follows:
├── __test__
├── assets
├── configs
├── modules
│ ├── common (Here is what is reused in various modules)
│ │ ├── components
│ │ │ ├── index.ts
│ │ │ ├── Layouts
│ │ │ └── (...generic components)
│ │ ├── hooks
│ │ │ ├── index.ts
│ │ │ └── (...generic hooks)
│ │ └── types
│ │ ├── index.ts
│ │ └── (...generic definitions)
│ │ └── utils
│ │ ├── index.ts
│ │ └── (...helpers or generic utilities)
│ └── users
│ ├── actions.ts (API calls, caches, redux, etc.)
│ ├── components
│ │ ├── index.ts
│ │ ├── Form.tsx (user form)
│ │ ├── Table.tsx (user table)
│ │ └── (...user components)
│ ├── hooks
│ │ ├── index.ts
│ │ ├── useUserActions.ts
│ │ └── (...user hooks)
│ ├── pages
│ │ ├── listing.tsx (user listing page)
│ │ ├── new.tsx (page to add a user)
│ │ ├── edit.tsx (page to edit a user)
│ │ ├── show.tsx (page to display a user)
│ │ └── (...other user pages)
│ └── types
│ ├── index.ts
│ ├── responses.ts (user API responses)
│ └── models.ts (definition of the user model)
├── pages
└── public
__tests__
: Tests to ensure that the app works correctly are included here.assets
: Here are the resources that need to be processed (e.g. SASS files, unminified images, SVGs, etc.)configs
: It is mainly used to store global settings that do not change often (e.g. main menu items, supported file types, etc.).modules
: Application-specific modules.pages
: Here we define the paths where the pages of the Next.js app will be.public
: These are the resources that will be copied as they are to the public root of the application.
Most of the source code is inside the modules
folder. When a new module needs to be added, it should be added here.
Here is a practical example: You need to add "notifications" to the application, the first step would be to add a new "module" that will live in modules/notifications
. Following this, you will need to add their respective parts such as components
, types
, hooks
, pages
, actions
, etc.
Each new module must have its own index index.ts
in the root, as well as in each of its sub-directories:
// modules/notifications/index.ts
export * from './actions';
export * from './components';
export * from './hooks';
export * from './types';
This allows us to have better control when importing parts from other modules:
import { useAuthActions } from '~/modules/auth';
import { DefaultLayout } from '~/modules/common';
import { UserForm } from '~/modules/users';
We would need to config tsconfig.json
or jsconfig.json
accordingly:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/modules/*": ["modules/*"]
}
}
}
For this way of organizing to be scalable, certain guidelines must be followed when naming variables.
components
: When a component belongs to a specific module it must have its model name at the beginning.- Examples:
UserTable
UserFilter
UserForm
- etc.
- Examples:
hooks
: Every hook must begin with the worduse
, followed by the model e.g.User
,Notification
, etc. (or if it is a common hook it can be omitted) and ending with the action or subject to be used.- Examples:
useUserActions
useEffectOnUpdate
useAuthUser
useSession
useAPIData
- etc.
- Examples:
actions
: The actions of a module should be named first with the verb to be performed (get
,update
,delete
, etc.) followed by the name of the module. Examples:updateUser
,createUser
,deleteNotification
. This in turn allows us to standardize the actions that can be performed in each model and control the consequences of these actions in one place (e.g. update caches, modify status, send notifications, etc.).
Having the pages separate from the directory provided by Next.js ensures that the application paths are independent of the views. This gives us some flexibility, for example: to change the user listing path from /config/users
to /users
, we simply move the /config/users/index.tsx
file to /users/index.tsx
without needing to modify the import
of the listing. In this case the index.ts
file simply re-exports the list to be displayed:
// pages/users/index.tsx
export { default } from '~/modules/users/pages/listing';
Or if you need to run some Next.js method like getServerSideProps
it is also re-exported from the same file:
// pages/users/index.tsx
export { default, getServerSideProps } from '~/modules/users/pages/listing';
Another use case would be that some view is visible in two different paths, for example: the detail of a user can be visible in /users/[userId]
or in /company/[companyId]/[userId]
.
// pages/users/[userId]/index.tsx
export { default } from '~/modules/users/pages/show';
// pages/company/[companyId]/[userId].tsx
export { default } from '~/modules/users/pages/show';