Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@zomars
Last active July 28, 2022 23:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zomars/ebf93c3281b714ae4048e9f43b0ff20e to your computer and use it in GitHub Desktop.
Save zomars/ebf93c3281b714ae4048e9f43b0ff20e to your computer and use it in GitHub Desktop.
Reusable Next.js project structure

File structure

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.
  • hooks: Every hook must begin with the word use, 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.
  • 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.).

Why have pages inside modules?

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';

More resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment