Использование с Next.js
FSD совместим с Next.js как в варианте App Router, так и в варианте Pages Router, если устранить главный конфликт — папки app и pages.
App Router
Заголовок раздела «App Router»Конфликт между FSD и Next.js в слое app
Заголовок раздела «Конфликт между FSD и Next.js в слое app»Next.js предлагает использовать папку app для определения маршрутов приложения. Он ожидает, что файлы в папке app будут соответствовать маршрутам. Этот механизм маршрутизации не соответствует концепции FSD, потому что невозможно сохранить плоскую структуру слайсов.
Чтоб решить эту проблему, перенесите Next.js-овскую папку app в корень проекта, а затем импортируйте FSD-страницы из src, где располагаются слои FSD, в Next.js-овскую папку app.
Вам также нужно будет добавить в корень проекта папку pages, иначе Next.js будет пытаться использовать src/pages в качестве Pages Router, даже если вы используете App Router, что приведёт к ошибкам при сборке проекта. Имеет смысл положить внутрь этой корневой папки pages файл README.md с описанием, почему эта папка нужна, даже когда она пустая.
Директорияapp
Директорияapi
Директорияget-example
- route.ts
Директорияexample
- page.tsx
Директорияpages
- README.md
Директорияsrc/
Директорияapp/
Директорияapi-routes/
- …
Директорияpages/
Директорияexample/
- index.ts
Директорияui/
- example.tsx
Директорияwidgets/
- …
Директорияfeatures/
- …
Директорияentities/
- …
Директорияshared/
- …
Пример ре-экспорта страницы из src/pages в Next.js-овском app:
export { ExamplePage as default, metadata } from '@/pages/example';Middleware
Заголовок раздела «Middleware»Если вы используете middleware в проекте, оно обязательно должно располагаться в корне проекта рядом с Next.js-овскими папками app и pages.
Instrumentation
Заголовок раздела «Instrumentation»Файл instrumentation.js позволяет отслеживать производительность и поведение вашего приложения. Если вы его используете, то он обязательно должен находиться в корне проекта по аналогии с middleware.js
Pages Router
Заголовок раздела «Pages Router»Конфликт между FSD и Next.js в слое pages
Заголовок раздела «Конфликт между FSD и Next.js в слое pages»Роуты страниц должны помещаться в папку pages в корне проекта по аналогии с папкой app для App Router. Структура внутри src, где располагаются папки слоёв, остаётся без изменений.
Директорияpages
- _app.tsx
Директорияapi
- example.ts
Директорияexample
- index.tsx
Директорияsrc
Директорияapp
Директорияcustom-app
- custom-app.tsx
Директорияapi-routes
- get-example-data.ts
Директорияpages
Директорияexample
- index.ts
Директорияui
- example.tsx
Директорияwidgets/
- …
Директорияfeatures/
- …
Директорияentities/
- …
Директорияshared/
- …
Пример ре-экспорта страницы из src/pages в Next.js-овском pages:
export { Example as default } from '@/pages/example';Кастомный компонент _app
Заголовок раздела «Кастомный компонент _app»Вы можете поместить ваш кастомный компонент App либо в src/app/_app либо в src/app/custom-app:
import type { AppProps } from 'next/app';
export const MyApp = ({ Component, pageProps }: AppProps) => { return ( <> <p>My Custom App component</p> <Component { ...pageProps } /> </> );};export { App as default } from '@/app/custom-app';Route Handlers (API-маршруты)
Заголовок раздела «Route Handlers (API-маршруты)»Используйте сегмент api-routes в слое app для работы c Route Handlers.
Будьте внимательны при написании бэкенд-кода в структуре FSD — FSD в первую очередь предназначен для фронтенда, и именно это люди будут ожидать в нём найти. Если вам нужно много эндпоинтов, попробуйте выделить их в отдельный пакет в монорепозитории.
import { getExamplesList } from '@/shared/db';
export const getExampleData = () => { try { const examplesList = getExamplesList();
return Response.json({ examplesList }); } catch { return Response.json(null, { status: 500, statusText: 'Ouch, something went wrong', }); }};export { getExampleData as GET } from '@/app/api-routes';import type { NextApiRequest, NextApiResponse } from 'next';
const config = { api: { bodyParser: { sizeLimit: '1mb', }, }, maxDuration: 5,};
const handler = (req: NextApiRequest, res: NextApiResponse<ResponseData>) => { res.status(200).json({ message: 'Hello from FSD' });};
export const getExampleData = { config, handler } as const;export { getExampleData } from './get-example-data';import { getExampleData } from '@/app/api-routes';
export const config = getExampleData.config;export default getExampleData.handler;Дополнительные рекомендации
Заголовок раздела «Дополнительные рекомендации»- Используйте сегмент
dbв слоеsharedдля описания запросов к БД и их дальнейшего использования в вышестоящих слоях. - Логику кэширования и ревалидации запросов лучше держать там же, где и сами запросы.