Architecture Overview
Server Runtime
Rendering Pipeline
Client Navigation
Caching and Export
Development Tools
Build and Configuration
Ecosystem Packages
Testing Infrastructure
How It Works
The following files were used as context for generating this wiki page:
Codemod transformations automate the upgrade process for Next.js applications by programmatically modifying source code, configuration files, and project structures across major version boundaries. These transformations address breaking changes, API deprecations, and architectural shifts—such as adopting asynchronous request APIs, migrating bundlers, and updating component conventions—thereby reducing manual refactoring overhead and ensuring compatibility with newer Next.js releases.
The @next/codemod command-line interface provides a unified execution environment for running individual AST transformations and orchestrating automated multi-step project upgrades. Built on top of Commander, the entry point dispatches commands to specialized modules, ensuring proper positional option parsing, interactive prompts via prompts, and git safety validation prior to file modification.
Sources: packages/next-codemod/bin/next-codemod.ts:1-105, packages/next-codemod/bin/transform.ts:1-11
Before any transformation writes changes to disk, checkGitStatus inspects the working directory using isGitClean.sync(process.cwd()). If uncommitted changes exist, the execution halts and exits with code 1 unless the --force flag is supplied.
checkGitStatus(force) → isGitClean.sync(cwd) ──(clean)──► execute transform
└──(dirty)─► exit(1) [or bypass if force]
Caution
Running codemods without a clean git directory or without passing --force terminates execution immediately. If the execution environment is not recognized as a git repository, checkGitStatus catches the error and treats the workspace as clean.
When invoking runTransform or suggestCodemods, the CLI interacts with users through prompts to select transformation targets, define file paths, and filter applicable codemods based on version differentials.
Sources: packages/next-codemod/bin/next-codemod.ts:36-46, packages/next-codemod/bin/transform.ts:123-148
Once files are expanded via globby and validation succeeds, runTransform constructs argument arrays for jscodeshift. Special transforms such as cra-to-next and next-lint-to-eslint-cli bypass jscodeshift entirely and invoke their default export functions directly with the expanded file paths and options.
const transformerPath = join(transformerDirectory, `${transformer}.js`)
if (transformer === 'cra-to-next') {
return require(transformerPath).default(filesExpanded, options)
}
if (transformer === 'next-lint-to-eslint-cli') {
return require(transformerPath).default(filesExpanded, options)
}For standard AST transforms, arguments configure parser options, ignore patterns, extensions, and the target transformer path before dispatching via execa.
let args = []
const { dry, print, runInBand, jscodeshift, verbose } = options
if (dry) {
args.push('--dry')
}
if (print) {
args.push('--print')
}
if (runInBand) {
args.push('--run-in-band')
}
if (verbose) {
args.push('--verbose=2')
}
args.push('--parser=tsx')
args.push('--ignore-pattern=**/node_modules/**')
args.push('--ignore-pattern=**/.next/**')
args.push('--extensions=tsx,ts,jsx,js')
args = args.concat(['--transform', transformerPath])
if (jscodeshift) {
args = args.concat(jscodeshift)
}
args = args.concat(filesExpanded)The next-async-request-api codemod orchestrates transformations for updating synchronous dynamic properties and request access patterns to leverage async/await and React's use hook. The top-level transform orchestrator coordinates multiple specialized sub-transforms sequentially across target source files.
export default function transform(file: FileInfo, api: API) {
const transforms = [transformDynamicProps, transformDynamicAPI]
return transforms.reduce<string>((source, transformFn) => {
const result = transformFn(source, api, file.path)
if (!result) {
return source
}
return result
}, file.source)
}The transformDynamicProps suite handles member access expressions on component props, checking whether accessed properties match target names and updating scopes accordingly. When transforming member access, awaitMemberAccessOfProp inspects functions for member expressions matching the prop name.
function awaitMemberAccessOfProp(
propIdName: string,
path: ASTPath<FunctionScope>,
j: API['jscodeshift']
) {
const functionBody = findFunctionBody(path)
const memberAccess = j(functionBody).find(j.MemberExpression, {
object: {
type: 'Identifier',
name: propIdName,
},
})
...Warning
If a parent function scope is synchronous and distinct from the target function itself, the codemod cannot convert it to async. In this case, it inserts an error comment directly into the code at the member access site rather than failing silently.
The codemod also inspects named exports and re-exports to flag components that need manual inspection when using params or searchParams.
Sources: packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts:177-224, packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts:228-246
The core component and routing transforms modernize legacy Next.js patterns by converting deprecated APIs, wrapping routing components, auto-naming components, and injecting missing imports. The suite includes codemods for next/link syntax updates, url to withRouter migration, next/image layout adjustments, component display naming, and automatic React imports.
Sources: packages/next-codemod/transforms/new-link.ts:1-123, packages/next-codemod/transforms/url-to-withrouter.ts:85-393, packages/next-codemod/transforms/next-image-experimental.ts:258-328, packages/next-codemod/transforms/name-default-component.ts:23-104, packages/next-codemod/transforms/add-missing-react-import.ts:51-90
The new-link transform targets imports from 'next/link', stripping deprecated legacyBehavior and passHref attributes. For child anchor elements (<a>), it extracts anchor properties, deduplicates them against existing <Link> attributes, pushes unique props to the <Link> element, and replaces the child anchor with its inner children.
// Before:
// <Link href="/about" legacyBehavior passHref><a className="link">About</a></Link>
// After:
// <Link href="/about" className="link">About</Link>Warning
If a link uses legacyBehavior but contains a child that cannot be resolved as an anchor tag, the codemod bails out of lifting props and injects an error block comment into the element's children.
The url-to-withrouter transform locates default exports and variable declarations referencing url via this.props.url or parameter props, renames identifiers to router, inserts withRouter wrapping via wrapNodeInFunction(), and adds the withRouter import.
function wrapNodeInFunction(j, functionName, args) {
const mappedArgs = args.map((node) => {
if (node.type === 'ClassDeclaration') {
node.type = 'ClassExpression'
}
return node
})
return j.callExpression(j.identifier(functionName), mappedArgs)
}Image codemods handle bidirectional imports between 'next/image', 'next/legacy/image', and 'next/future/image'. The experimental image transform replaces legacy layout, objectFit, and objectPosition props with inline styles and generated sizes attributes based on layout mappings.
Sources: packages/next-codemod/transforms/next-image-experimental.ts:20-31, packages/next-codemod/transforms/next-image-to-legacy-image.ts:12-93
The name-default-component transform infers component identifiers from the file's base name, converting it to PascalCase via camelCase() and verifying validity with isValidIdentifier(). If an identifier collision occurs, it appends 'Component' to the name.
const camelCase = (value: string): string => {
const val = value.replace(/[-_\s.]+(.)?/g, (_match, chr) =>
chr ? chr.toUpperCase() : ''
)
return val.slice(0, 1).toUpperCase() + val.slice(1)
}
const isValidIdentifier = (value: string): boolean =>
/^[a-zA-ZÀ-ÿ][0-9a-zA-ZÀ-ÿ]+$/.test(value)The add-missing-react-import transform checks files for React member expression usages without a corresponding default import. If usage is detected, it either attaches a React default specifier to an existing 'react' import declaration or unshifts a new import React from 'react' statement into the top-level program body.
Sources: packages/next-codemod/transforms/add-missing-react-import.ts:10-49, packages/next-codemod/transforms/add-missing-react-import.ts:59-88
The configuration and flag cleanup transforms automate the evolution of Next.js projects by updating next.config.js settings, migrating experimental Turbopack options, converting middleware features to proxy equivalents, and stripping deprecated experimental features. These codemods inspect Abstract Syntax Trees (ASTs) via jscodeshift to modify static objects, configuration functions, arrow functions, and assignment expressions.
Sources: packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts:1-25, packages/next-codemod/transforms/middleware-to-proxy.ts:1-35
The next-experimental-turbo-to-turbopack transform restructures legacy experimental Turbopack options inside Next.js configuration files. It locates experimental.turbo property blocks and migrates properties to a top-level turbopack object while routing performance and debugging flags to experimental.turbopack* namespaces. Unsupported options such as memory limits are removed entirely from both old and intermediate locations.
Sources: packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts:1-40, packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts:98-166
Warning
If an experimental configuration object becomes completely empty after extracting turbo properties and removing unsupported memory limits, the codemod deletes the entire experimental property from the configuration object.
The middleware-to-proxy transform updates configuration properties, type imports, and runtime segment configs when renaming middleware mechanisms to proxy equivalents. It maps next/server type imports (NextMiddleware to NextProxy, MiddlewareConfig to ProxyConfig) and rewrites config properties including middlewarePrefetch, middlewareClientMaxBodySize, externalMiddlewareRewritesResolve, and skipMiddlewareUrlNormalize.
Sources: packages/next-codemod/transforms/middleware-to-proxy.ts:19-32, packages/next-codemod/transforms/middleware-to-proxy.ts:108-163
Additional cleanups remove deprecated experimental flags. The app-dir-runtime-config-experimental-edge transform targets App Router page, layout, and route files, locating named runtime exports configured with 'experimental-edge' and rewriting their string literal value to 'edge'. Similarly, the remove-experimental-ppr transform strips experimental_ppr variable declarations, direct named exports, and specifiers from App Router files.
Sources: packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts:4-41, packages/next-codemod/transforms/remove-experimental-ppr.ts:4-78
Note
The app-dir-runtime-config-experimental-edge transform requires an exact match of a single named export containing the string literal 'experimental-edge'; if multiple runtime exports or different values are present, the file is bypassed without modification.
Metadata and package import migrations handle structural refactoring for Next.js features, updating font package namespaces, Open Graph imports, viewport configurations, and ESLint flat configuration files. These transforms normalize third-party or legacy integration paths to modern conventions using AST manipulation tools.
Sources: packages/next-codemod/transforms/next-lint-to-eslint-cli.ts:319-522, packages/next-codemod/transforms/metadata-to-viewport-export.ts:4-95, packages/next-codemod/transforms/next-og-import.ts:6-52, packages/next-codemod/transforms/built-in-next-font.ts:4-47
The built-in-next-font transform locates import declarations referencing legacy @next/font source strings and updates them to the built-in next/font package scope. It targets root packages, google subpaths, and local font variants.
Similarly, the next-og-import transform parses files for next/server import declarations containing ImageResponse. When found, it splits the specifiers, isolating ImageResponse into a dedicated import statement from next/og while preserving remaining specifiers under next/server.
Note
The next-og-import transform splits specifiers dynamically: ImageResponse elements map to next/og, while all other identifiers remain tied to next/server.
The metadata-to-viewport-export transform extracts mobile viewport configurations from the legacy metadata named export object and promotes them into an independent viewport export. It searches the metadata object declaration for specific properties (viewport, colorScheme, themeColor), extracts them, filters them out of the metadata object, and constructs a new named viewport constant export when modifications occur.
Warning
If a file lacks any matching viewport, colorScheme, or themeColor properties inside its metadata export, the transformer makes no modifications and returns the original source string unmodified.
Sources: packages/next-codemod/transforms/metadata-to-viewport-export.ts:20-22, packages/next-codemod/transforms/metadata-to-viewport-export.ts:69-72
The next-lint-to-eslint-cli transform updates ESLint flat configuration files (eslint.config.js) by replacing FlatCompat wrapper calls with direct config imports. It inspects compat.extends(...) and compat.config({ extends: [...] }) constructs, mapping known string literals to direct identifier spreads.
Sources: packages/next-codemod/transforms/next-lint-to-eslint-cli.ts:334-358, packages/next-codemod/transforms/next-lint-to-eslint-cli.ts:400-420
Caution
Unrecognized non-Next.js string configurations encountered inside compat.extends arrays are preserved as wrapping compat.extends() or compat.config() calls rather than dropped.
Sources: packages/next-codemod/transforms/next-lint-to-eslint-cli.ts:344-357, packages/next-codemod/transforms/next-lint-to-eslint-cli.ts:415-431
The Create React App (CRA) framework migration codemod automates the end-to-end repository conversion of CRA and Vite projects into structured Next.js applications. Orchestrated by the CraTransform class and specialized jscodeshift transformers, the tool validates source directories, detects project frameworks, transforms custom DOM root rendering into Next.js compatible component wrappers, and scaffolds required routing and configuration files.
Sources: packages/next-codemod/transforms/cra-to-next.ts:31-83, packages/next-codemod/lib/cra-to-next/index-to-component.ts:4-102
The project transformation sequence executes through a precise chain of validation, AST rewriting, and scaffolding steps. Invoking CraTransform.transform() proceeds through the following named operations:
CraTransform.transform() → runJscodeshift(indexTransformPath) → runJscodeshift(globalCssTransformPath) → fs.promises.mkdir() → this.updatePackageJson() → this.createNextConfig() → this.updateGitIgnore() → this.createPages()
During index-to-component execution, the transformer checks for default React DOM imports and render call expressions, validating render boundaries before exporting NextIndexWrapper.
Sources: packages/next-codemod/transforms/cra-to-next.ts:85-148, packages/next-codemod/lib/cra-to-next/index-to-component.ts:21-84
Warning
If multiple ReactDOM render roots or nested render calls are detected during the index transformation, CraTransform immediately halts execution via fatalMessage() to prevent invalid component structures.
Sources: packages/next-codemod/transforms/cra-to-next.ts:102-113, packages/next-codemod/lib/cra-to-next/index-to-component.ts:58-61
The CraTransform constructor inspects project dependencies, package managers, and configuration files to establish execution flags.
Sources: packages/next-codemod/transforms/cra-to-next.ts:31-83, packages/next-codemod/lib/cra-to-next/index-to-component.ts:4-7