Skip to main content

Migrating from Next.js

ness migrate next # in the Next.js project
ness migrate next --dry-run # print the plan without touching a file

The command requires a clean git working tree, so the migration can be reviewed as a diff. It writes MIGRATION.md listing every file it moved, every import it rewrote, and everything left for you.

Only the App Router is migrated. Convert a Pages Router project to the App Router in Next first.

What carries over unchanged​

The route conventions are nearly the same, which is what makes this mechanical rather than a rewrite:

Next.jsNess.js
app/page.tsxapp/routes/page.tsx
app/layout.tsx (nested)app/routes/.../layout.tsx
app/layout.tsx (root)app/root.tsx
app/loading.tsxapp/routes/.../loading.tsx
app/error.tsxapp/routes/.../error.tsx
app/not-found.tsxapp/routes/.../not-found.tsx
app/api/x/route.tsapp/routes/api/x/route.ts
[id], [...rest], [[...rest]], (group), _privateidentical

Route Handlers keep their shape too: a route module exporting GET, POST, and so on is dispatched by method in both frameworks.

Imports rewritten automatically​

Next.jsNess.jsNote
next/linkreact-routerhref becomes to
next/image@nessframework/core
next/script@nessframework/core
next/navigation hooks@nessframework/coresame names
next/cache@nessframework/cacheunstable_cache becomes cached
next/font/local@nessframework/core/font

What needs a human​

These are reported, not guessed at. A codemod that silently reshapes code it did not fully understand is worse than one that tells you what it skipped.

Server Components. A Next async page component becomes a loader in an adjacent page.server file plus a synchronous component reading useLoaderData(). The split depends on what the component awaits.

// Next
export default async function Page() {
const products = await db.product.findMany();
return <List products={products} />;
}
app/routes/page.server.ts
export async function loader() {
return db.product.findMany();
}
app/routes/page.tsx
export default function Page() {
return <List products={useLoaderData()} />;
}

Server Actions. A "use server" function becomes an action export in page.server, submitted through <Form>.

redirect() and notFound(). In Ness these are thrown from a loader or action, from @nessframework/core/server/responses.

cookies() and headers(). Next reads these from an implicit request. Read them from the request argument your loader or action already receives.

NextRequest / NextResponse. These are the Web-standard Request and Response globals in Ness; drop the import.

generateStaticParams. List the paths under router.prerender in ness.config.mjs.

generateMetadata. Export meta from the route, or use @nessframework/core/metadata.

next.config.js. redirects, rewrites, headers, and images move into the server section of ness.config.mjs; the option shapes match.

Middleware. Next middleware runs once per request. Ness middleware is scoped to a route segment β€” usually a closer fit, but the placement has to be chosen.

After migrating​

ness typegen
ness build

Review the diff, work through MIGRATION.md, then delete it.