D55PSG official website built in NextJS and Typescript with Zustand for global state, TailwindCSS for styling and Framer Motion for animations.
- Node.js v18.13.0
- NextJS v13.2.4
- React.js v18.2.0
- Typescript v4.9.4
- TailwindCSS v3.2.7
- Framer Motion v10.8.5
- Zustand v4.3.6
- StandardJS v17.0.0
Using NextJS ESLint with StandardJS, is necessary the following configuration for eslintrc file to ensure ESLint works propertly with React components:
"extends": [
"next/core-web-vitals",
"./node_modules/standard/eslintrc.json"
]
I'm using the src folder for keep code at a different level than configurations and miscellaneous files.
This is the structure I use for my NextJS projects:
We can express the above image with the following structure:
- src
- components/app (app)
- core (core)
- components (domain)
- modules (domain)
- pages (pages)
Contains components that is being used by the special pages/_app.tsx
NextJS file, like:
- Layouts
- The store of the global state
- Scripts
- And more...
We could develop our folder structure in this way:
- src
- components/app
- AppLayout.tsx
- AppProvider.tsx
- GoogleSeacrchScript.tsx
- ...
I prefer keep the components on a single folder, using the vertical slice philosofy, the app components can contain and export configuration data. Some configuration data are in the modules/app
folder, this folder is part of the domain layer and its used for domain and pages layers.
This layer is used to place all unrelated to our domain
The core and the domain of a project, in my view, should be kept well separate.
Hard rule: the core layer can never import anything from the domain layer.
If something within the core layer needs something from the domain, it probably does not belong to the core layer.
For example:
- The database connector
- The utils for authentification
- The email provider
- And more...
The core layer provides the domain with the necessary tools for the application to work, for example, authenticating, querying the DB, or sending emails. But does not know how the consumer uses these tools.
We could develop our folder structure in this way:
- src
- core
- database.ts
- auth.ts
- mailing.ts
- ...
The business domain is the area of control or a sphere of knowledge, such as, the group of entities, relationships, and behaviors of the business model, implemented as code.
We can express the domain layer with the following structure:
- src
- components
- modules
What should add to this folder anything about the domain that isn't a component, like:
- Custom hooks
- Configurations
- Contexts
- Queries
- And all utilities related to the domain
These utilities are typically used within components.
We could develop our folder structure in this way:
- src
- modules
- search
- hooks
- config
- queries
- ...
- navigation
- hooks
- config
- ...
The components that make up our pages. Furthermore, these components are highly tied to the domain.
They're business-logic rich, and we can build them using the reusable UI components from core.
The business-logic side of things, such as queries, or functions that mutate data, are all imported from modules so that they can be reusable across components.
We could develop our folder structure in this way:
- src
- components
- input
- core
- Button.tsx
- CallToAction.tsx
- navigation
- core
- Link.tsx
- Menu.tsx
The components folder can contain a core folder for each module, this contain small and reutilizable components used for create complex componentes out of core folders and pages, such like atomic design pattern: atoms (components/core) -> molecules (components) -> organism (pages).
NextJS's router is file-system based, the folder pages is a Next-specific directory to place our API.
We could develop our folder structure in this way:
- src
- pages
- api
- Search
- [keyword].ts
- _app.tsx
- index.tsx
- product
- [id].tsx
Something important to clarify is the rules I have around importing between layers.
Typically, an inner layer cannot import from an outer layer, that means that core cannot import from the domain layer, and the latter cannot import from pages.
Wrong
Ok
This rule can ensure your core is decoupled from the domain to avoid cyclical dependencies and keep your architecture untangled.
We can ensure that we're using the rules above correctly when importing files in our application.
EsLint can help us by adding the following configuration, can automatically warn you that you're importing from the wrong paths:
"rules": {
"import/no-restricted-paths": [
"error",
{
"zones": [
{
"target": "./src/core",
"from": "./src/components"
},
{
"target": "./src/core",
"from": "./src/modules"
},
{
"target": "./src/core",
"from": "./src/pages"
},
{
"target": "./src/components",
"from": "./src/pages"
},
{
"target": "./src/modules",
"from": "./src/components"
},
{
"target": "./src/modules",
"from": "./src/pages"
},
{
"target": "./src/core",
"from": "./src/components/app"
},
{
"target": "./src/components",
"from": "./src/components/app"
},
{
"target": "./src/modules",
"from": "./src/components/app"
}
]
}
]
}
If you need to use a components on component/app
that are in the same folder, add the following comment above the import statement:
// eslint-disable-next-line import/no-restricted-paths
import GoogleSearchScript from './GoogleSearchScript'
export default AppLayout () {
return (
<Head>
<GoogleSearchScript />
</Head>
)
}
I recommend use pnpm because it hold all the packages at a global (centralized) store and use them if needed by other projects too by creating hard links to it.
- Clone the project
$ git clone https://github.com/saufth/d55psg.git/
- Install the dependencies:
$ pnpm install (recommended)
# or
$ yarn
# or
$ npm install
- Run the development server
pnpm dev (recommended)
# or
yarn dev
# or
npm run dev
Open http://localhost:3000/ with your browser to see the result.