Skip to content

Usage with Next.js

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.

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.

  • Directoryapp Next.js app folder
    • Directoryapi/
      • Directoryget-example/
        • route.ts
    • Directoryexample/
      • page.tsx
  • Directorysrc/
    • Directory_app/ FSD layer
      • Directoryapi-routes/ API routes
    • Directory_pages/ FSD layer
      • Directoryexample/
        • index.ts
        • Directoryui/
          • example.tsx
    • Directorywidgets/
    • Directoryfeatures/
    • Directoryentities/
    • Directoryshared/

Example of re-exporting a page from src/_pages in the Next.js app:

app/example/page.tsx
export { ExamplePage as default, metadata } from '@/_pages/example';

In Next.js App Router, modules that can be used on the client and server-only modules may exist together within a single slice. If a server-only module is exported from index.ts, server-only side effects can propagate into the client module graph when a Client Component imports that slice, which can lead to build errors.

When this problem occurs, add index.server.ts to the public API.

  • index.server.ts: Modules that must only be imported on the server, such as Server Components or data access functions marked with server-only

If you use middleware in your project, it must be located in the project root alongside the Next.js app and pages folders.

The instrumentation.js file allows you to monitor the performance and behavior of your application. If you use it, it must be located in the project root, similar to middleware.js.

Conflict between FSD and Next.js in the pages layer

Section titled “Conflict between FSD and Next.js in the pages layer”

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.

  • Directorypages/ Pages folder (Next.js)
    • _app.tsx
    • Directoryapi/
      • example.ts API route re-export
    • Directoryexample/
      • index.tsx
  • Directorysrc/
    • Directory_app/ FSD layer
      • Directorycustom-app/
        • custom-app.tsx Custom App component
      • Directoryapi-routes/
        • get-example-data.ts API route
    • Directory_pages/ FSD layer
      • Directoryexample/
        • index.ts
        • Directoryui/
          • example.tsx
    • Directorywidgets/
    • Directoryfeatures/
    • Directoryentities/
    • Directoryshared/

Example of re-exporting a page from src/_pages in the Next.js pages:

pages/example/index.tsx
export { Example as default } from '@/_pages/example';

You can place your Custom App component in src/_app/_app or src/_app/custom-app:

src/_app/custom-app/custom-app.tsx
import type { AppProps } from 'next/app';
export const MyApp = ({ Component, pageProps }: AppProps) => {
return (
<>
<p>My Custom App component</p>
<Component { ...pageProps } />
</>
);
};
pages/_app.tsx
export { App as default } from '@/_app/custom-app';

Use the api-routes segment in the _app layer to work with Route Handlers.

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. If you need a lot of endpoints, consider separating them into a different package in a monorepo.

src/_app/api-routes/get-example-data.ts
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',
});
}
};
app/api/example/route.ts
export { getExampleData as GET } from '@/_app/api-routes';
  • Use the db segment in the shared layer to describe database queries and their further use in higher layers.
  • Caching and revalidating queries logic is better kept in the same place as the queries themselves.