Documentation
Migrate a Next.js app
Add vinext alongside an existing Next.js setup without rewriting the application.
Migration is non-destructive.
vinext initleaves your Next.js scripts and source files in place, then adds parallel*:vinextscripts for evaluation.
Migrate with an AI agent
To have an AI agent investigate compatibility issues and guide the migration, install the optional vinext Agent Skill. It works with Claude Code, OpenCode, Cursor, Codex, and other AI coding tools:
pnpm dlx skills add cloudflare/vinextThen open your Next.js project in your agent and ask it to migrate this project to vinext. The skill runs the compatibility check, installs dependencies, generates config, and starts the dev server, and flags anything that needs manual attention. When it runs vinext init for Cloudflare, it asks you for the cache and image optimization choices rather than picking them itself.
Migrate manually
vinext init is the direct, repeatable path. Run these steps yourself to migrate without an agent.
1. Check compatibility
pnpm dlx vinext checkThe scanner reports known unsupported configuration, imports, and project patterns. Also check the compatibility dashboard and open issues for behavior specific to your application.
2. Initialize vinext
pnpm dlx vinext initChoose Cloudflare or Node when prompted. The command installs the required packages, adds ESM and Vite configuration, and creates deployment config for the selected platform.
For a non-interactive run, pass the choices explicitly:
pnpm dlx vinext init --platform=cloudflare --cdn-cache=none --data-cache=none --image-optimization=none
pnpm dlx vinext init --platform=nodeFor Cloudflare, agents and CI must pass all three of --cdn-cache, --data-cache, and --image-optimization. The Cloudflare command above chooses no cache and no image optimization; see Run init without prompts for the other values.
3. Run both toolchains
pnpm dev:vinext
pnpm build:vinext
pnpm devCompare the routes and workflows your application depends on. Start with authentication, middleware, mutations, caching, and deployment bindings rather than only checking the home page.
Styling
CSS Modules
If the project contains CSS Modules, init adds vite-css-modules and configures stable class names so PostCSS runs before class scoping. It updates inline Vite config objects automatically. For dynamic configs (factories, spreads, identifier exports, or CommonJS), init stops before changing the project rather than guessing how to rewrite the config. To continue with init, convert the Vite config to an inline object; otherwise finish the migration manually with equivalent CSS settings:
import { createHash } from "node:crypto";
import path from "node:path";
import { patchCssModules } from "vite-css-modules";
import { defineConfig } from "vite";
import vinext from "vinext";
export default defineConfig({
plugins: [patchCssModules({ exportMode: "default" }), vinext()],
css: {
modules: {
generateScopedName(name: string, filename: string) {
const relativePath = path
.relative(import.meta.dirname, filename.replace(/\?.*$/, ""))
.replaceAll("\\", "/");
return `_${name}_${createHash("sha256").update(relativePath).digest("hex").slice(0, 7)}`;
},
},
},
});
Keep any other plugins and options in your existing config; place patchCssModules before vinext.