|
| 1 | +--- |
| 2 | +title: experimental.adapterPath |
| 3 | +description: Configure a custom adapter for Next.js to hook into the build process with modifyConfig and onBuildComplete callbacks. |
| 4 | +--- |
| 5 | + |
| 6 | +Next.js provides an experimental API that allows you to create custom adapters to hook into the build process. This is useful for deployment platforms or custom build integrations that need to modify the Next.js configuration or process the build output. |
| 7 | + |
| 8 | +## Configuration |
| 9 | + |
| 10 | +To use an adapter, specify the path to your adapter module in `experimental.adapterPath`: |
| 11 | + |
| 12 | +```js filename="next.config.js" |
| 13 | +/** @type {import('next').NextConfig} */ |
| 14 | +const nextConfig = { |
| 15 | + experimental: { |
| 16 | + adapterPath: require.resolve('./my-adapter.js'), |
| 17 | + }, |
| 18 | +} |
| 19 | + |
| 20 | +module.exports = nextConfig |
| 21 | +``` |
| 22 | + |
| 23 | +## Creating an Adapter |
| 24 | + |
| 25 | +An adapter is a module that exports an object implementing the `NextAdapter` interface: |
| 26 | + |
| 27 | +```typescript |
| 28 | +export interface NextAdapter { |
| 29 | + name: string |
| 30 | + modifyConfig?: ( |
| 31 | + config: NextConfigComplete, |
| 32 | + ctx: { |
| 33 | + phase: PHASE_TYPE |
| 34 | + } |
| 35 | + ) => Promise<NextConfigComplete> | NextConfigComplete |
| 36 | + onBuildComplete?: (ctx: { |
| 37 | + routes: { |
| 38 | + headers: Array<ManifestHeaderRoute> |
| 39 | + redirects: Array<ManifestRedirectRoute> |
| 40 | + rewrites: { |
| 41 | + beforeFiles: Array<ManifestRewriteRoute> |
| 42 | + afterFiles: Array<ManifestRewriteRoute> |
| 43 | + fallback: Array<ManifestRewriteRoute> |
| 44 | + } |
| 45 | + dynamicRoutes: ReadonlyArray<ManifestRoute> |
| 46 | + } |
| 47 | + outputs: AdapterOutputs |
| 48 | + projectDir: string |
| 49 | + repoRoot: string |
| 50 | + distDir: string |
| 51 | + config: NextConfigComplete |
| 52 | + nextVersion: string |
| 53 | + }) => Promise<void> | void |
| 54 | +} |
| 55 | +``` |
| 56 | + |
| 57 | +### Basic Adapter Structure |
| 58 | + |
| 59 | +Here's a minimal adapter example: |
| 60 | + |
| 61 | +```js filename="my-adapter.js" |
| 62 | +/** @type {import('next').NextAdapter} */ |
| 63 | +const adapter = { |
| 64 | + name: 'my-custom-adapter', |
| 65 | + |
| 66 | + async modifyConfig(config, { phase }) { |
| 67 | + // Modify the Next.js config based on the build phase |
| 68 | + if (phase === 'phase-production-build') { |
| 69 | + return { |
| 70 | + ...config, |
| 71 | + // Add your modifications |
| 72 | + } |
| 73 | + } |
| 74 | + return config |
| 75 | + }, |
| 76 | + |
| 77 | + async onBuildComplete({ |
| 78 | + routes, |
| 79 | + outputs, |
| 80 | + projectDir, |
| 81 | + repoRoot, |
| 82 | + distDir, |
| 83 | + config, |
| 84 | + nextVersion, |
| 85 | + }) { |
| 86 | + // Process the build output |
| 87 | + console.log('Build completed with', outputs.pages.length, 'pages') |
| 88 | + |
| 89 | + // Access different output types |
| 90 | + for (const page of outputs.pages) { |
| 91 | + console.log('Page:', page.pathname, 'at', page.filePath) |
| 92 | + } |
| 93 | + |
| 94 | + for (const apiRoute of outputs.pagesApi) { |
| 95 | + console.log('API Route:', apiRoute.pathname, 'at', apiRoute.filePath) |
| 96 | + } |
| 97 | + |
| 98 | + for (const appPage of outputs.appPages) { |
| 99 | + console.log('App Page:', appPage.pathname, 'at', appPage.filePath) |
| 100 | + } |
| 101 | + |
| 102 | + for (const prerender of outputs.prerenders) { |
| 103 | + console.log('Prerendered:', prerender.pathname) |
| 104 | + } |
| 105 | + }, |
| 106 | +} |
| 107 | + |
| 108 | +module.exports = adapter |
| 109 | +``` |
| 110 | + |
| 111 | +## API Reference |
| 112 | + |
| 113 | +### `modifyConfig(config, context)` |
| 114 | + |
| 115 | +Called for any CLI command that loads the next.config to allow modification of the configuration. |
| 116 | + |
| 117 | +**Parameters:** |
| 118 | + |
| 119 | +- `config`: The complete Next.js configuration object |
| 120 | +- `context.phase`: The current build phase (see [phases](/docs/app/api-reference/config/next-config-js#phase)) |
| 121 | + |
| 122 | +**Returns:** The modified configuration object (can be async) |
| 123 | + |
| 124 | +### `onBuildComplete(context)` |
| 125 | + |
| 126 | +Called after the build process completes with detailed information about routes and outputs. |
| 127 | + |
| 128 | +**Parameters:** |
| 129 | + |
| 130 | +- `routes`: Object containing route manifests for headers, redirects, rewrites, and dynamic routes |
| 131 | +- `outputs`: Detailed information about all build outputs organized by type |
| 132 | +- `projectDir`: Absolute path to the Next.js project directory |
| 133 | +- `repoRoot`: Absolute path to the detected repository root |
| 134 | +- `distDir`: Absolute path to the build output directory |
| 135 | +- `config`: The final Next.js configuration (with `modifyConfig` applied) |
| 136 | +- `nextVersion`: Version of Next.js being used |
| 137 | + |
| 138 | +## Output Types |
| 139 | + |
| 140 | +The `outputs` object contains arrays of different output types: |
| 141 | + |
| 142 | +### Pages (`outputs.pages`) |
| 143 | + |
| 144 | +React pages from the `pages/` directory: |
| 145 | + |
| 146 | +```typescript |
| 147 | +{ |
| 148 | + type: 'PAGES' |
| 149 | + id: string // Route identifier |
| 150 | + filePath: string // Path to the built file |
| 151 | + pathname: string // URL pathname |
| 152 | + runtime: 'nodejs' | 'edge' |
| 153 | + assets: Record<string, string> // Traced dependencies |
| 154 | + config: { |
| 155 | + maxDuration?: number |
| 156 | + preferredRegion?: string | string[] |
| 157 | + } |
| 158 | +} |
| 159 | +``` |
| 160 | + |
| 161 | +### API Routes (`outputs.pagesApi`) |
| 162 | + |
| 163 | +API routes from `pages/api/`: |
| 164 | + |
| 165 | +```typescript |
| 166 | +{ |
| 167 | + type: 'PAGES_API' |
| 168 | + id: string |
| 169 | + filePath: string |
| 170 | + pathname: string |
| 171 | + runtime: 'nodejs' | 'edge' |
| 172 | + assets: Record<string, string> |
| 173 | + config: { |
| 174 | + maxDuration?: number |
| 175 | + preferredRegion?: string | string[] |
| 176 | + } |
| 177 | +} |
| 178 | +``` |
| 179 | + |
| 180 | +### App Pages (`outputs.appPages`) |
| 181 | + |
| 182 | +React pages from the `app/` directory with `page.{js,ts,jsx,tsx}`: |
| 183 | + |
| 184 | +```typescript |
| 185 | +{ |
| 186 | + type: 'APP_PAGE' |
| 187 | + id: string |
| 188 | + filePath: string |
| 189 | + pathname: string |
| 190 | + runtime: 'nodejs' | 'edge' |
| 191 | + assets: Record<string, string> |
| 192 | + config: { |
| 193 | + maxDuration?: number |
| 194 | + preferredRegion?: string | string[] |
| 195 | + } |
| 196 | +} |
| 197 | +``` |
| 198 | + |
| 199 | +### App Routes (`outputs.appRoutes`) |
| 200 | + |
| 201 | +API and metadata routes from `app/` with `route.{js,ts,jsx,tsx}`: |
| 202 | + |
| 203 | +```typescript |
| 204 | +{ |
| 205 | + type: 'APP_ROUTE' |
| 206 | + // ... same structure as APP_PAGE |
| 207 | +} |
| 208 | +``` |
| 209 | + |
| 210 | +### Prerenders (`outputs.prerenders`) |
| 211 | + |
| 212 | +ISR-enabled routes and static prerenders: |
| 213 | + |
| 214 | +```typescript |
| 215 | +{ |
| 216 | + type: 'PRERENDER' |
| 217 | + id: string |
| 218 | + pathname: string |
| 219 | + parentOutputId: string // ID of the source page/route |
| 220 | + groupId: number // Revalidation group identifier |
| 221 | + fallback?: { |
| 222 | + filePath: string |
| 223 | + initialStatus?: number |
| 224 | + initialHeaders?: Record<string, string | string[]> |
| 225 | + initialExpiration?: number |
| 226 | + initialRevalidate?: number |
| 227 | + postponedState?: string // PPR postponed state |
| 228 | + } |
| 229 | + config: { |
| 230 | + allowQuery?: string[] // Allowed query parameters |
| 231 | + allowHeader?: string[] // Allowed headers for ISR |
| 232 | + bypassFor?: RouteHas[] // Cache bypass conditions |
| 233 | + renderingMode?: RenderingMode |
| 234 | + bypassToken?: string |
| 235 | + } |
| 236 | +} |
| 237 | +``` |
| 238 | + |
| 239 | +### Static Files (`outputs.staticFiles`) |
| 240 | + |
| 241 | +Static assets and auto-statically optimized pages: |
| 242 | + |
| 243 | +```typescript |
| 244 | +{ |
| 245 | + type: 'STATIC_FILE' |
| 246 | + id: string |
| 247 | + filePath: string |
| 248 | + pathname: string |
| 249 | +} |
| 250 | +``` |
| 251 | + |
| 252 | +### Middleware (`outputs.middleware`) |
| 253 | + |
| 254 | +Middleware function (if present): |
| 255 | + |
| 256 | +```typescript |
| 257 | +{ |
| 258 | + type: 'MIDDLEWARE' |
| 259 | + id: string |
| 260 | + filePath: string |
| 261 | + pathname: string |
| 262 | + runtime: 'nodejs' | 'edge' |
| 263 | + assets: Record<string, string> |
| 264 | + config: { |
| 265 | + maxDuration?: number |
| 266 | + preferredRegion?: string | string[] |
| 267 | + matchers?: MiddlewareMatcher[] |
| 268 | + } |
| 269 | +} |
| 270 | +``` |
| 271 | + |
| 272 | +## Use Cases |
| 273 | + |
| 274 | +Common use cases for adapters include: |
| 275 | + |
| 276 | +- **Deployment Platform Integration**: Automatically configure build outputs for specific hosting platforms |
| 277 | +- **Asset Processing**: Transform or optimize build outputs |
| 278 | +- **Monitoring Integration**: Collect build metrics and route information |
| 279 | +- **Custom Bundling**: Package outputs in platform-specific formats |
| 280 | +- **Build Validation**: Ensure outputs meet specific requirements |
0 commit comments