Skip to content

Commit 4c6bf39

Browse files
authored
docs: advise prefixed fsd layers for nextjs (#927)
1 parent 4f5ae68 commit 4c6bf39

1 file changed

Lines changed: 49 additions & 51 deletions

File tree

src/content/docs/docs/guides/tech/with-nextjs.mdx

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,45 @@ sidebar:
44
order: 1
55
---
66

7-
import { Tabs, TabItem, FileTree } from '@astrojs/starlight/components';
7+
import { Tabs, TabItem, FileTree, Aside } from '@astrojs/starlight/components';
88

9-
FSD is compatible with Next.js in both the App Router version and the Pages Router version if you solve the main conflict — the `app` and `pages` folders.
9+
<Aside type="caution">
10+
To avoid conflicts, rename **both** `app` and `pages` FSD layers to `_app` and `_pages`, regardless of which router you use.
11+
</Aside>
1012

11-
## App Router \{#app-router\}
12-
13-
### Conflict between FSD and Next.js in the `app` layer \{#conflict-between-fsd-and-nextjs-in-the-app-layer\}
13+
## src Folder
1414

15-
Next.js suggests using the `app` folder to define application routes. It expects files in the `app` folder to correspond to pathnames. This routing mechanism **does not align** with the FSD concept, as it's not possible to maintain a flat slice structure.
15+
Next.js expects special `app` or `pages` folders either in the root of the project or in the `src` folder. Generally, it is easier to place Next.js folders in the root of the project so that the `src` folder contains only FSD code, but it is not mandatory.
1616

17-
The solution is to move the Next.js `app` folder to the project root and import FSD pages from `src`, where the FSD layers are, into the Next.js `app` folder.
17+
## App Router \{#app-router\}
1818

19-
You will also need to add a `pages` folder to the project root, otherwise Next.js will try to use `src/pages` as the Pages Router even if you use the App Router, which will break the build. It's also a good idea to put a `README.md` file inside this root `pages` folder describing why it is necessary, even though it's empty.
19+
Next.js uses the `app` folder for the App Router and the `pages` folder for the Pages Router, which conflicts with FSD layer names. To solve this conflict, use prefixed names for FSD layers, such as `_app` instead of `app` and `_pages` instead of `pages`. This approach is also compatible with the official [linter](https://github.com/feature-sliced/steiger).
2020

2121
<FileTree>
22-
- app App folder (Next.js)
23-
- api
24-
- get-example
25-
- route.ts
26-
- example
27-
- page.tsx
28-
- pages Empty pages folder (Next.js)
29-
- README.md
30-
- src
31-
- app
32-
- api-routes API routes
33-
- pages
34-
- example
35-
- index.ts
36-
- ui
37-
- example.tsx
38-
- widgets/
39-
- features/
40-
- entities/
41-
- shared/
22+
- app Next.js app folder
23+
- api/
24+
- get-example/
25+
- route.ts
26+
- example/
27+
- page.tsx
28+
- src/
29+
- _app/ FSD layer
30+
- api-routes/ API routes
31+
- _pages/ FSD layer
32+
- example/
33+
- index.ts
34+
- ui/
35+
- example.tsx
36+
- widgets/
37+
- features/
38+
- entities/
39+
- shared/
4240
</FileTree>
4341

44-
Example of re-exporting a page from `src/pages` in the Next.js `app`:
42+
Example of re-exporting a page from `src/_pages` in the Next.js `app`:
4543

4644
```tsx title="app/example/page.tsx"
47-
export { ExamplePage as default, metadata } from '@/pages/example';
45+
export { ExamplePage as default, metadata } from '@/_pages/example';
4846
```
4947

5048
### Server and client public APIs \{#server-and-client-public-apis\}
@@ -70,40 +68,40 @@ The `instrumentation.js` file allows you to monitor the performance and behavior
7068
Routes should be placed in the `pages` folder in the root of the project, similar to `app` folder for the App Router. The structure inside `src` where the layer folders are located remains unchanged.
7169

7270
<FileTree>
73-
- pages Pages folder (Next.js)
71+
- pages/ Pages folder (Next.js)
7472
- _app.tsx
75-
- api
73+
- api/
7674
- example.ts API route re-export
77-
- example
75+
- example/
7876
- index.tsx
79-
- src
80-
- app
81-
- custom-app
77+
- src/
78+
- _app/ FSD layer
79+
- custom-app/
8280
- custom-app.tsx Custom App component
83-
- api-routes
81+
- api-routes/
8482
- get-example-data.ts API route
85-
- pages
86-
- example
83+
- _pages/ FSD layer
84+
- example/
8785
- index.ts
88-
- ui
86+
- ui/
8987
- example.tsx
9088
- widgets/
9189
- features/
9290
- entities/
9391
- shared/
9492
</FileTree>
9593

96-
Example of re-exporting a page from `src/pages` in the Next.js `pages`:
94+
Example of re-exporting a page from `src/_pages` in the Next.js `pages`:
9795

9896
```tsx title="pages/example/index.tsx"
99-
export { Example as default } from '@/pages/example';
97+
export { Example as default } from '@/_pages/example';
10098
```
10199

102100
### Custom `_app` component \{#custom-_app-component\}
103101

104-
You can place your Custom App component in `src/app/_app` or `src/app/custom-app`:
102+
You can place your Custom App component in `src/_app/_app` or `src/_app/custom-app`:
105103

106-
```tsx title="src/app/custom-app/custom-app.tsx"
104+
```tsx title="src/_app/custom-app/custom-app.tsx"
107105
import type { AppProps } from 'next/app';
108106

109107
export const MyApp = ({ Component, pageProps }: AppProps) => {
@@ -117,12 +115,12 @@ export const MyApp = ({ Component, pageProps }: AppProps) => {
117115
```
118116

119117
```tsx title="pages/_app.tsx"
120-
export { App as default } from '@/app/custom-app';
118+
export { App as default } from '@/_app/custom-app';
121119
```
122120

123121
## Route Handlers (API routes) \{#route-handlers-api-routes\}
124122

125-
Use the `api-routes` segment in the `app` layer to work with Route Handlers.
123+
Use the `api-routes` segment in the `_app` layer to work with Route Handlers.
126124

127125
Be mindful when writing backend code in the FSD structure — FSD is primarily intended for frontends, meaning that's what people will expect to find.
128126
If you need a lot of endpoints, consider separating them into a different package in a monorepo.
@@ -131,7 +129,7 @@ If you need a lot of endpoints, consider separating them into a different packag
131129

132130
<TabItem value="app-router" label="App Router">
133131

134-
```tsx title="src/app/api-routes/get-example-data.ts"
132+
```tsx title="src/_app/api-routes/get-example-data.ts"
135133
import { getExamplesList } from '@/shared/db';
136134

137135
export const getExampleData = () => {
@@ -149,14 +147,14 @@ export const getExampleData = () => {
149147
```
150148

151149
```tsx title="app/api/example/route.ts"
152-
export { getExampleData as GET } from '@/app/api-routes';
150+
export { getExampleData as GET } from '@/_app/api-routes';
153151
```
154152

155153
</TabItem>
156154

157155
<TabItem value="pages-router" label="Pages Router">
158156

159-
```tsx title="src/app/api-routes/get-example-data.ts"
157+
```tsx title="src/_app/api-routes/get-example-data.ts"
160158
import type { NextApiRequest, NextApiResponse } from 'next';
161159

162160
const config = {
@@ -175,12 +173,12 @@ const handler = (req: NextApiRequest, res: NextApiResponse<ResponseData>) => {
175173
export const getExampleData = { config, handler } as const;
176174
```
177175

178-
```tsx title="src/app/api-routes/index.ts"
176+
```tsx title="src/_app/api-routes/index.ts"
179177
export { getExampleData } from './get-example-data';
180178
```
181179

182180
```tsx title="app/api/example.ts"
183-
import { getExampleData } from '@/app/api-routes';
181+
import { getExampleData } from '@/_app/api-routes';
184182

185183
export const config = getExampleData.config;
186184
export default getExampleData.handler;

0 commit comments

Comments
 (0)