# Framework Integration

How to set up FSD within specific frameworks. Covers directory placement,
routing integration, and framework-specific path alias configuration.


## General Principle

Place FSD layers inside `src/` to avoid naming conflicts with framework
directories. The FSD `app/` and `pages/` layers are **not** the same as
framework directories with the same names (e.g., Next.js `app/`).

All FSD projects follow the same `@/<layer>/*` path alias convention. The
exact configuration differs by framework. See each framework section
below. Astro is the one exception, using a single `@/*` alias instead.


## Next.js

FSD is compatible with both the App Router and the Pages Router. The main
conflict is that Next.js owns the `app/` and `pages/` folder names, and both
collide with FSD layer names. Resolve the conflict by moving the Next.js
routing folders to the project root and importing FSD pages from `src/` into
them.

### App Router

The Next.js `app/` folder lives at the project root. **Always create an
empty `pages/` folder at the project root as well, even when you only use
the App Router.** Without it, Next.js tries to use `src/pages/` as the Pages
Router and the build breaks. Add a `pages/README.md` explaining why the
folder exists.

#### Directory structure

```text
my-nextjs-project/
  app/                     ← Next.js App Router (routing only)
    layout.tsx
    page.tsx
    profile/
      page.tsx
    api/
      get-example/
        route.ts
  pages/                   ← Empty, required even for App Router
    README.md
  src/
    app/                   ← FSD app layer
      providers/
        index.tsx          ← All providers (QueryClient, theme, etc.)
      styles/
        globals.css
      api-routes/          ← Route Handler implementations (see below)
        index.ts
        get-example-data.ts
    pages/                 ← FSD pages layer
      home/
        ui/HomePage.tsx
        index.ts
      profile/
        ui/ProfilePage.tsx
        model/profile.ts
        api/fetch-profile.ts
        index.ts
    widgets/               ← FSD widgets layer (when needed)
    features/              ← FSD features layer (when needed)
    entities/              ← FSD entities layer (when needed)
    shared/                ← FSD shared layer
      ui/
      lib/
      api/
      db/                  ← Database queries (see below)
```

#### Wiring Next.js routes to FSD pages

```typescript
// app/layout.tsx
import { Providers } from '@/app/providers';
import '@/app/styles/globals.css';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body><Providers>{children}</Providers></body>
    </html>
  );
}

// app/example/page.tsx: re-export the FSD page (component + metadata)
export { ExamplePage as default, metadata } from '@/pages/example';
```

Always re-export both the component and `metadata`. Route files contain no logic.

### Pages Router

The Pages Router uses `pages/` at the project root. The FSD `src/` tree is
unchanged. Routes re-export from `src/pages/`:

```text
my-nextjs-project/
  pages/                   ← Next.js Pages Router (routing only)
    _app.tsx
    api/example.ts         ← API route re-export
    example/index.tsx
  src/
    app/
      custom-app/          ← Custom App component
      api-routes/          ← Route Handler implementations
    pages/
      example/
        ui/example.tsx
        index.ts
```

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

// pages/_app.tsx: re-export the custom App from src/app/custom-app
export { App as default } from '@/app/custom-app';
```

The custom App component itself lives in `src/app/custom-app/` and exports
`App` from its public API like any other FSD slice.

### Middleware and instrumentation

`middleware.js` and `instrumentation.js` must live at the **project root**,
next to the Next.js `app/` and `pages/` folders. Next.js will not detect
them inside `src/`.

### Route Handlers (API routes)

Use a dedicated `api-routes` segment in the FSD `app/` layer
(`src/app/api-routes/`) to host the actual request handlers. The Next.js
`app/api/*/route.ts` (App Router) or `pages/api/*.ts` (Pages Router) files
become thin re-exports.

**App Router:**

```typescript
// 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',
    });
  }
};

// src/app/api-routes/index.ts
export { getExampleData } from './get-example-data';

// app/api/example/route.ts
export { getExampleData as GET } from '@/app/api-routes';
```

**Pages Router:**

```typescript
// src/app/api-routes/get-example-data.ts
import type { NextApiRequest, NextApiResponse } from 'next';

const config = { api: { bodyParser: { sizeLimit: '1mb' } }, maxDuration: 5 };
const handler = (req: NextApiRequest, res: NextApiResponse) =>
  res.status(200).json({ message: 'Hello from FSD' });

export const getExampleData = { config, handler } as const;

// app/api/example.ts
import { getExampleData } from '@/app/api-routes';
export const config = getExampleData.config;
export default getExampleData.handler;
```

FSD is primarily a frontend methodology. If `api-routes` grows to many
endpoints, consider moving the backend to a separate package in a monorepo.

### Database access

Place database queries in a `db` segment in `shared/` (`src/shared/db/`).
Co-locate caching and revalidation logic with the queries themselves.

### Path aliases

```json
// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/app/*": ["src/app/*"],
      "@/pages/*": ["src/pages/*"],
      "@/widgets/*": ["src/widgets/*"],
      "@/features/*": ["src/features/*"],
      "@/entities/*": ["src/entities/*"],
      "@/shared/*": ["src/shared/*"]
    }
  }
}
```

Next.js reads `tsconfig.json` paths automatically. No `next.config.js`
alias configuration is needed.

### Server Components and Public API splitting

FSD layers work inside both Server and Client Components. However, the
standard single `index.ts` public API can cause problems in RSC environments
because re-exporting client and server code from the same entry point may
trigger bundler errors or unintended boundary crossings.

Split the public API into multiple entry points per environment:

```text
entities/user/
  model/
    user.ts
  ui/
    UserAvatar.tsx          ← 'use client', uses hooks
    UserProfileCard.tsx     ← Server Component, no hooks
  api/
    user-queries.server.ts  ← Server-only data fetching
  index.ts                  ← Shared exports (types, pure functions)
  index.client.ts           ← Client component exports
  index.server.ts           ← Server component + server-only exports
```

```typescript
// entities/user/index.ts: shared (types, pure logic, no components)
export type { User } from "./model/user";
export { formatUserName } from "./model/user";

// entities/user/index.client.ts: client components only
export { UserAvatar } from "./ui/UserAvatar";

// entities/user/index.server.ts: server components + server-only code
export { UserProfileCard } from "./ui/UserProfileCard";
export { fetchUser } from "./api/user-queries.server";
```

```typescript
// Consumers import from the appropriate entry point:

// In a Server Component (pages/profile/ui/ProfilePage.tsx)
import { UserProfileCard } from "@/entities/user/index.server";
import type { User } from "@/entities/user";

// In a Client Component (features/comment/ui/CommentAuthor.tsx)
import { UserAvatar } from "@/entities/user/index.client";
```

**Rules for split public APIs:**

1. **`index.ts`**: types, constants, and pure functions that work in both
   environments. Default import path.
2. **`index.client.ts`**: components using `'use client'`, hooks, or
   browser APIs.
3. **`index.server.ts`**: Server Components and server-only data fetching.
4. The `index.[env].ts` pattern generalises beyond RSC. Any meta-framework
   with distinct runtime environments can use it (e.g., `index.edge.ts`).
   Verified for Next.js App Router; Nuxt and Astro compatibility is under
   review.
5. Steiger support for multiple entry points is available or coming in an
   upcoming release. If Steiger flags these files, check for version
   updates.

**When NOT to split:** A slice with no client/server boundary concerns uses
a single `index.ts`. Split only when a slice actually has both client and
server exports.


## Nuxt 3

### Directory structure

```text
my-nuxt-project/
  pages/                   ← Nuxt file-based routing
    index.vue              ← Route entry, imports from FSD pages layer
    profile.vue
  src/
    app/                   ← FSD app layer
      providers/
    pages/                 ← FSD pages layer
      home/
        ui/HomePage.vue
        index.ts
      profile/
        ui/ProfilePage.vue
        model/profile.ts
        index.ts
    shared/                ← FSD shared layer
      ui/
      lib/
      api/
```

### Wiring Nuxt routes to FSD pages

```vue
<!-- pages/index.vue: thin route entry -->
<template>
  <HomePage />
</template>
<script setup>
import { HomePage } from "@/pages/home";
</script>
```

### Path aliases

In addition to the standard `tsconfig.json` mapping, Nuxt requires explicit
runtime aliases in `nuxt.config.ts`:

```typescript
// nuxt.config.ts
import { resolve } from "path";

export default defineNuxtConfig({
  alias: {
    "@/app": resolve(__dirname, "src/app"),
    "@/pages": resolve(__dirname, "src/pages"),
    "@/widgets": resolve(__dirname, "src/widgets"),
    "@/features": resolve(__dirname, "src/features"),
    "@/entities": resolve(__dirname, "src/entities"),
    "@/shared": resolve(__dirname, "src/shared"),
  },
});
```


## Vite + React

### Directory structure

```text
my-vite-project/
  src/
    app/                   ← FSD app layer
      providers/
      router.tsx
      styles/
      main.tsx             ← Entry point
    pages/
    shared/
  index.html
  vite.config.ts
  tsconfig.json
```

### Path aliases

Mirror the standard `tsconfig.json` mapping in `vite.config.ts` so the
Vite resolver agrees with TypeScript:

```typescript
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { resolve } from "path";

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@/app": resolve(__dirname, "src/app"),
      "@/pages": resolve(__dirname, "src/pages"),
      "@/widgets": resolve(__dirname, "src/widgets"),
      "@/features": resolve(__dirname, "src/features"),
      "@/entities": resolve(__dirname, "src/entities"),
      "@/shared": resolve(__dirname, "src/shared"),
    },
  },
});
```


## Create React App (CRA)

CRA is no longer actively maintained. **Migrate to Vite for new projects.**

If you must stay on CRA, path aliases require ejecting or using `craco` to
override the webpack config:

```javascript
// craco.config.js
const path = require("path");

module.exports = {
  webpack: {
    alias: {
      "@/app": path.resolve(__dirname, "src/app"),
      "@/pages": path.resolve(__dirname, "src/pages"),
      "@/widgets": path.resolve(__dirname, "src/widgets"),
      "@/features": path.resolve(__dirname, "src/features"),
      "@/entities": path.resolve(__dirname, "src/entities"),
      "@/shared": path.resolve(__dirname, "src/shared"),
    },
  },
};
```


## Astro

Astro uses `src/pages/` for file-based routing, which collides with the FSD
`pages/` layer. Move the FSD pages layer to `src/_pages/` (with the
underscore prefix) and reserve `src/pages/` for Astro routes.

### Directory structure

```text
my-astro-project/
  src/
    pages/                 ← Astro routing (thin entry points)
      404.astro
      index.astro
    _pages/                ← FSD pages layer
      home/
        ui/HomePage.astro
        index.ts
    widgets/
    features/
    entities/
    shared/
```

### Wiring Astro routes to FSD pages

The Astro route file imports and renders the FSD page, nothing else:

```astro
// src/pages/index.astro
import { HomePage } from '@/_pages/home';
<HomePage />
```

### Path aliases (tsconfig.json)

Astro projects use a single `@/*` alias instead of one alias per layer. This
is the convention the FSD Astro guide recommends:

```json
{
  "extends": "astro/tsconfigs/strict",
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
```

Imports then reference the layer path directly: `@/_pages/home`,
`@/shared/ui`, `@/entities/user`.

### Working with integrations

Some Astro integrations (for example, Starlight) use content collections
that expect content in fixed folders such as `src/content/docs/`. If the
integration does not allow the path to be changed, leave it as-is. The
content folder lives alongside FSD layers without collision:

```text
src/
  _pages/                  ← FSD pages layer
  content/                 ← Integration content (Starlight, etc.)
    docs/
      getting-started.md
  shared/                  ← FSD shared layer
```

Let the integration handle its own routing and rendering, while FSD layers
manage application-specific code.


## Key Reminders for All Frameworks

1. **FSD lives in `src/`**: root-level `app/` and `pages/` belong to the
   framework's routing, not FSD.
2. **Framework route files are thin wrappers**: they import and render FSD
   page components. Business logic stays in FSD pages.
3. **Path aliases are required**: configure both the bundler and
   `tsconfig.json`.
4. **Pages First still applies**: regardless of framework, start with code
   in FSD `pages/` and extract only when needed.
