Let's get straight to it
nx g @nx/next:app site
Here we are creating the next site application, this will be our frontend.
Choose None
we don't want any of these format provided, we will set up tailwind later.
Choose app router, if you would like to learn more about it visit the docs
Tailwind Setup
Now we need to set up tailwind, we are also using daisyui
component library built upon tailwind
npm install -D tailwindcss
npm i -D daisyui@latest
nx g @nx/react:setup-tailwind --project=site
now we go to tailwind.config.js
inside the site app and paste the following boilerplate
import { createGlobPatternsForDependencies } from '@nx/react/tailwind'
import join from 'node:path'
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
join(
__dirname,
'{app}/**/*!(*.stories|*.spec).{ts,tsx,html}'
),
...createGlobPatternsForDependencies(__dirname),
],
theme: {
extend: {},
},
daisyui: {
themes: [],
},
plugins: [require('daisyui')],
}
now go to global.css
and replace the existing code with the following
@tailwind base;
@tailwind components;
@tailwind utilities;
App structure
Our structure will be a mix of featured sliced design and redux app structure
Firstly create a src
dir inside apps/site
then move the app inside the src
Next will be able to capture it automatically.
You will end up with folder structure like the following:
apps/site
├── public
├── src
│ └── app
Now we will add some other folders to structure our app.
apps/site
├── public
├── src
│ └── app
│ └── layout.tsx
│ └── global.css
│ └── page.tsx
│ └── [pageName]
│ └── layout.tsx
│ └── page.tsx
│ └── hooks
│ └── components
│ └── shared
│ └── store
│ └── helpers
│ └── features
│ └── [featureName]
│ └── api
│ └── ui
│ └── model
| └── slice.ts
| └── types.ts
│ └── lib
│ └── commands
│ └── queries
│ └── index.ts
Now let's explain each one responsibility:
app
: This is the app directory from Next.js check this Next routing & layouts
shared
: This will contain any business logic or fetch library that will be shared across the features.
It will host the redux store and provider,
Side NOTE: make sure to wrap the children of layout.tsx component inside the store provider and add
use client
directive atop of the module; to be able to useRedux Provider
as it lives only onReact Client Component
.
hooks
: This will contain the React hooks, These are the ones that are only related to this monorepo, if you have for example check authorization hook this should be a global hook where you need to create shared next app to store it.
components
: This will act like the widget
directory in the FSD the idea here you are storing here the pure components or components that access multiple selectors only to render, it must not contain business logic, these the presentational by design.
features
: This is heavily inspired by redux features directory but it is a little different.
It combines presentational entities
.(e.g., User, Product, Order) and user interactions, actions that bring business value to the user.(e.g. SendComment, AddToCart, UsersSearch). you can do split them they same way described here does. But here we are combining because we are not creating interfaces entirely for the frontend, instead we are sharing them via shared
lib.
You can think of it as
cross-cutting
domain but for UI.
The features contains the following directories
-
lib
: Contains the data manipulation & presentational logic.This library and the entire front-end is totally isolated from the business logic, as we in later chapters show where it will live.
commands
: You can think of them as the GraphQL Mutations related to the feature, let's say the feature iscart
the commands would be for exampleaddToCart
,removeFromCart
andcheckoutCart
, all commands start with a commanding verb likecreate
,modify
,patch
,replace
etc.queries
: These hold the presentational responsibilitiesui
dir. Meaning they will decide what to present or what needed to command the backend. The queries will start with the verbget
, assuming we have a feature called product we will have queries likegetProductByName
,getRecommendedProducts
.ui
: It is the feature's React components basically they are the components that depend upon the feature store where it will render some jsx, there are some unwritten rules here we need to address them, think of them as a simple but with store accessibility and anonClick
action.
- Components Should never take props or depend on other features' slices this violates SRP
- Components will present final results cooked in the slice, meaning it will not depend
useState
or thelib
. The decision of what it will present was already made and stored inside the slice.
-
model
: this dir will contain two modules -
slice.ts
: where we will create the redux slice types.ts
: this directory contains the extended types for the ui from theshared
typesapi
: This directory will contain the async redux actions, this directory will depend upon thelib
,commands
,queries
heavily.
Let's say we are working on an Markdown editor and we are creating an API that gets the file from the server look at the following:You will need to take the fetch lib or
executeQuery
from theshared
dirThe query statement and params will be managed in
queries
modules.lib
will manage data post processing before the api 'stores' the result into the sliceThe
ui
will be accessing the respectedslice
and manages presenting the UI.
There are general rules to be followed here
All functions are
camelCase
, components arePascalCase
and alldir
arekebab-case
-
outside the feature directory nothing can be imported directly, we are only exporting the ui components and the store, in this regard we are applying the following eslint rule in the coming parts of this series.
Note each feature has one barrel file, if you put barrel file in
ui
folder and import it in the feature one, it may cause issues with bundling All feature ui components are
react client components
meaning each module will start withuse client
, because simply we are utilizing the user slice aka user state.
So Try to but in the feature component as much less UI as possible and leave the main components to take care the rest while utilizingRSC
What else you would do to improve our template?
We as Engineers love to ask ourselves what else we can improve.
Here are some tips but not limited to:
Separate related components into a an independent
lib
this way need to import them dynamically and this can improve FCPIf the features are growing and changing, more teams are created with this rule, you need to look at various Micro-Frontend options, look at Module Federation where you can split your
app
intomirco-apps
each has its own responsibilities and deployed independently, the special thing with module federation is that modules are imported in runtime.Create an UI
lib
where will host all UI components needed for the entire app, this way you can share the same theme and colors across the app.
In summary, the provided guide not only aids in building a solid foundation for a frontend application but also emphasizes the importance of architectural decisions that can enhance collaboration, development speed, and the overall user experience. By embracing the recommended practices and considering future enhancements, developers can create a robust and adaptable frontend solution.
Top comments (0)