Type something to search...
Next.js Codemods

Next.js Codemods

A codemod is a programmatic transformation applied across your codebase — the entire point being that when Next.js renames or restructures an API, you don't manually open every affected file and fix it by hand. This article catalogs the actual transforms Next.js provides, spanning all the way from version 6 to the current 16.3, because the specific one you need depends entirely on which version boundary you're crossing.

The basic invocation

npx @next/codemod <transform> <path>

<transform> is the specific transform's name, <path> is the file or directory to apply it to. Two flags are worth using before committing to any transform on real, uncommitted work: --dry runs the transform without touching any files at all, and --print shows you the changed output for direct review — both genuinely worth reaching for on a codebase without a clean git state to fall back on if a transform does something unexpected.

16.3: Cache Components adoption codemods

cache-components-instant-false

npx @next/codemod@canary cache-components-instant-false ./app

This one is specifically built to support incremental Cache Components adoption, covered in depth in this series' dedicated migration article. It adds export const instant = false to every page, layout, and default file in your app directory that doesn't already export instant — letting you enable cacheComponents globally and then remove these opt-outs route by route, at your own pace, rather than needing every route ready simultaneously. It correctly skips Client Components and any file that already declares instant explicitly.

+ // TODO: Cache Components adoption. Refactor this route so this opt-out can be removed.
+ // See: https://nextjs.org/docs/app/guides/migrating-to-cache-components
+ export const instant = false
+
  export default function Page() {
    return <h1>Hello</h1>
  }

Worth flagging directly since it's a genuinely easy mistake: in a src/ project, pass ./src/app, not ./app — passing the wrong path doesn't error, it just silently reports 0 ok, which looks deceptively like "nothing needed changing" rather than "I looked in the wrong place entirely." Always check the reported file count against what you'd actually expect before trusting a 0 ok result.

remove-partial-prefetch

npx @next/codemod@canary remove-partial-prefetch ./app

The cleanup counterpart once you've enabled partialPrefetching globally — it removes the now-redundant export const prefetch = 'partial' from your page/layout files, since that per-route opt-in stops doing anything useful once the behavior is already the default everywhere. It's precise about scope: only the literal 'partial' value gets removed; other values like prefetch = 'force-disabled' are left untouched, since those still mean something distinct even with Partial Prefetching enabled globally.

16.0: Proxy rename and API stabilization

middleware-to-proxy

npx @next/codemod@latest middleware-to-proxy .

This is very likely the single most consequential codemod in this whole list if you're upgrading an existing project to 16, given how central the Middleware → Proxy rename is to this version. It handles the rename comprehensively — the file itself (middleware.tsproxy.ts), the exported function name (middlewareproxy), and every related next.config.js property (experimental.middlewarePrefetchexperimental.proxyPrefetch, experimental.middlewareClientMaxBodySizeexperimental.proxyClientMaxBodySize, experimental.externalMiddlewareRewritesResolveexperimental.externalProxyRewritesResolve, and skipMiddlewareUrlNormalizeskipProxyUrlNormalize).

// Before: middleware.ts
export function middleware() {
  return NextResponse.next();
}

// After: proxy.ts
export function proxy() {
  return NextResponse.next();
}

Given how many properties and naming conventions this touches simultaneously, this is exactly the kind of transform worth running with --dry first on anything you don't have a clean commit to fall back on.

remove-unstable-prefix

npx @next/codemod@latest remove-unstable-prefix .

A mechanical cleanup for APIs that have graduated out of their unstable_ naming once they stabilized — unstable_cacheTag becoming plain cacheTag, for instance. Simple in scope, but genuinely tedious to do manually across a codebase with many call sites.

next-lint-to-eslint-cli

npx @next/codemod@canary next-lint-to-eslint-cli .

Migrates a project off the next lint wrapper command onto the plain ESLint CLI directly, generating an eslint.config.mjs with Next.js's recommended configuration, updating your package.json lint script to call eslint . instead, and adding whatever ESLint dependencies that requires — while preserving any existing ESLint configuration it finds rather than clobbering it.

15.0: Edge runtime naming, async Dynamic APIs, and geo/IP

app-dir-runtime-config-experimental-edge

App Router–specific. Converts the Route Segment Config value runtime = 'experimental-edge' to the now-stable runtime = 'edge' — a naming cleanup once edge runtime support graduated out of its experimental designation.

next-async-request-api

npx @next/codemod@latest next-async-request-api .

This is the big one for anyone upgrading through version 15 specifically — cookies(), headers(), and draftMode() from next/headers all became asynchronous in that release, a breaking change covered in full in this series' dedicated version-15 guide. This codemod handles the transformation automatically wherever it safely can, converting a synchronous call site to properly await the now-async API, or wrapping it with React.use() where that's the more appropriate fit for the surrounding code:

// Before
const token = cookies().get("token");

// After
const token = (await cookies()).get("token");

It extends specifically to params and searchParams access inside page.js, layout.js, route.js, default.js, and the generateMetadata/generateViewport functions — detecting property access on those props and converting the surrounding function to async, awaiting the now-Promise-typed prop directly:

// Before
export default function Page({
  searchParams,
}: {
  searchParams: { value: string };
}) {
  const { value } = searchParams;
}

// After
export default async function Page(props: {
  searchParams: Promise<{ value: string }>;
}) {
  const searchParams = await props.searchParams;
  const { value } = searchParams;
}

Where an automatic fix genuinely isn't possible — commonly, inside a Client Component, which can't simply become async the way a Server Component can — the codemod adds either a TypeScript typecast or an explanatory comment flagging exactly what needs manual review, rather than silently leaving broken code behind. These markers are worth taking seriously rather than dismissing as codemod noise: your build will actually error until these specific comments are explicitly resolved and removed, which is a deliberate design choice ensuring nothing half-migrated slips through unnoticed.

next-request-geo-ip

npx @next/codemod@latest next-request-geo-ip .

Migrates the geo and ip properties that used to live directly on NextRequest over to the separate @vercel/functions package, installing that dependency and rewriting call sites automatically:

// Before
const { geo, ip } = req;

// After
import { geolocation, ipAddress } from "@vercel/functions";
const geo = geolocation(req);
const ip = ipAddress(req);

14.0 and earlier: the historical record

Worth knowing these exist even if you're not touching a codebase old enough to need them directly — if you're maintaining something that's been upgraded incrementally over several major versions rather than rewritten fresh, one of these older transforms might still be exactly what you need for a lingering piece of legacy code that never got fully migrated at the time.

next-og-import (14.0) moves ImageResponse imports from next/server to next/og, matching where Dynamic OG Image Generation actually lives now. metadata-to-viewport-export (14.0) splits viewport-related fields (like themeColor) out of the metadata export into their own dedicated viewport export, reflecting the framework's split between the two concerns. built-in-next-font (13.2) uninstalls the standalone @next/font package entirely and rewrites imports to the framework's now built-in next/font. next-image-to-legacy-image (13.0) renames next/image imports from Next.js 10–12 to next/legacy/image, freeing up the next/image name for the redesigned component that shipped in 13 — with next/future/image renamed the other direction, to plain next/image. next-image-experimental (13.0) is explicitly marked as a dangerous migration from next/legacy/image to the modern next/image, converting layout-related props (layout, objectFit, objectPosition) into inline styles and stripping props that no longer apply — "dangerous" here specifically means it's a best-effort transform likely to need manual visual review afterward, not something to run and walk away from unchecked. new-link (13.0) strips the <a> tag that used to be required as <Link>'s child, since that pattern became unnecessary once <Link> started rendering its own anchor directly.

Going back further: cra-to-next (11) migrates an entire Create React App project into a Next.js Pages Router structure, starting from client-only rendering to sidestep any window-related SSR breakage before gradually adopting Next.js-specific features. add-missing-react-import (10) adds a missing import React from 'react' wherever needed for the (then-new) JSX transform to function. name-default-component (9) converts anonymous default-exported components into named ones — a small but meaningful fix, since Fast Refresh specifically depends on components having stable names to correctly preserve state across edits. withamp-to-config (8) converted the old withAmp higher-order component into page-level config — worth noting explicitly that built-in AMP support, and this codemod along with it, were removed entirely in Next.js 16, so it's purely historical at this point, not something to reach for even on a very old codebase. url-to-withrouter (6) is the oldest transform in the list, migrating away from the long-deprecated automatically-injected url prop on top-level pages toward the withRouter HOC pattern instead.

Key Takeaways

VersionNotable codemodWhat it solves
16.3cache-components-instant-falseIncremental Cache Components adoption, route by route
16.0middleware-to-proxyThe Middleware → Proxy rename, files/exports/config together
15.0next-async-request-apicookies()/headers()/draftMode() becoming async
14.0next-og-importImageResponse moving to next/og
13.0next-image-to-legacy-imageFreeing up next/image for the redesigned component

The practical workflow this whole catalog supports: run npx @next/codemod upgrade first (covered in the Upgrade Guides overview article) to handle the common case automatically, and come back to this specific list when you're chasing down one particular legacy pattern the bundled upgrade didn't catch, or when you're deliberately running an individual transform in isolation with --dry first to understand exactly what it would change before committing to it.

Tags :
Share :

Related Posts

Can Next.js Be Used with GraphQL?

Can Next.js Be Used with GraphQL?

Next.js and GraphQL are two powerful technologies that have gained significant traction in the web development community. Next.js, a React-based fram

Dive Deeper
How does Next.js differ from Create React App?

How does Next.js differ from Create React App?

In the world of modern web development, React.js has emerged as a dominant force due to its flexibility, performance, and extensive ecosystem. Two po

Dive Deeper
How does Next.js handle image optimization?

How does Next.js handle image optimization?

In modern web development, image optimization plays a critical role in enhancing user experience and improving site performance. Large, unoptimized i

Dive Deeper