This guide moves a Next.js application from Vercel to Cloudflare Workers with vinext ↗︎. vinext reimplements the Next.js API on Vite and deploys to Workers.
Your app/, pages/, next.config.*, and public/ directories stay the same. next dev and next build keep working after the migration.
- vinext aims to support Next.js 14 and later. Partial Prerendering support is in progress. To check your application's compatibility with vinext, run
npx vinext check. - vinext uses
cloudflare.config.tsinstead of a Wrangler configuration file, and deploys with thecfCLI. The Worker name is theworker.namevalue in that file. - Run
vinext initdirectly instead ofwrangler setup. Automatic project configuration with Wrangler configures OpenNext, not vinext. - Review the steps in Before you begin that apply to every framework.
Copy this prompt into your coding agent. The agent stops before it deploys.
Migrate this Next.js app from Vercel to Cloudflare Workers using vinext (https://github.com/cloudflare/vinext).
1. Check the installed "next" version in package.json and the lockfile. Do not use `npx wrangler setup`, because it configures OpenNext.
2. Run `npx vinext check` and summarize the report. Stop and ask me if there are critical incompatibilities.
3. Choose the init flags. `vinext init` fails without a terminal unless you pass all of them:
- --name <repository name>. Without it, the Worker uses the package.json "name".
- Caching: if the app has no ISR (export const revalidate, fetch next.revalidate), no revalidatePath or revalidateTag, and no "use cache", use --cdn-cache=none --data-cache=none. Otherwise, use --cdn-cache=response-store --response-store-mode=self-contained --data-cache=none. Workers Response Store caches both responses and data, so do not add --data-cache=kv.
- Images: use --image-optimization=cloudflare-images only if next/image renders raster (PNG, JPEG, WebP, AVIF) or remote images. Otherwise, use --image-optimization=none.
Then run: npx vinext init --platform=cloudflare --name <name> --cdn-cache=<value> --data-cache=<value> --image-optimization=<value>
Read every file that init creates or changes. Add ".cloudflare/types" to tsconfig.json "include". Add ".cloudflare/**", ".vinext/**", and "dist/**" to the ESLint ignores. Do not add "nodejs_compat" to compatibilityFlags. A compatibility date of 2026-08-04 or later already enables it. Do not register @vitejs/plugin-rsc manually. Do not add a wrangler.jsonc file.
4. Do not modify app/ or pages/ beyond the replacements in step 5. Do not rewrite next/* imports. Do not delete next.config.*.
5. Replace Vercel features:
- Move vercel.json redirects, rewrites, and headers into next.config redirects(), rewrites(), and headers(). List any crons and ask me how to handle them.
- Replace @vercel/kv with Workers KV, @vercel/blob with R2, @vercel/postgres with Hyperdrive or D1 (ask me), and @vercel/edge-config with KV. Declare bindings in cloudflare.config.ts and read them with `import { env } from "cloudflare:workers"`. Run `npx cf workers types` after you change bindings.
- Remove @vercel/analytics and @vercel/speed-insights, or ask me about Cloudflare Web Analytics.
- Replace native modules such as sharp, @resvg/resvg-js, and @napi-rs/canvas.
6. Verify:
- Run `<pm> run dev:vinext -- --port <free port>` and check the main routes.
- Run `<pm> run build:vinext`, then `<pm> run start:vinext -- --port <free port>`, and check the routes again. This runs the built Worker locally.
- Confirm that lint, type checking, and `next dev` still work.
Rules: work on a new git branch; use the repository's package manager; never print secret values; do not deploy, log in, or change DNS.
When you finish, list the files you changed, the Vercel features that were replaced or are unsupported, and the secrets I must set. Give me the deploy commands: `npx cf auth login`, then `npx @vinext/cloudflare deploy --dry-run`, then `npx @vinext/cloudflare deploy`. Remind me that NEXT_PUBLIC_* values must exist at build time.vinext also provides a migrate-to-vinext agent skill. To install it, run npx skills add cloudflare/vinext.
-
Check that your application is compatible with vinext:
npx vinext checkyarn dlx vinext checkpnpx vinext check -
Initialize vinext with the cache and image options your application needs. All three options are required when you run
vinext initwithout an interactive terminal.npx vinext init --platform=cloudflare --name <YOUR_WORKER_NAME> --cdn-cache=none --data-cache=none --image-optimization=noneyarn dlx vinext init --platform=cloudflare --name <YOUR_WORKER_NAME> --cdn-cache=none --data-cache=none --image-optimization=nonepnpx vinext init --platform=cloudflare --name <YOUR_WORKER_NAME> --cdn-cache=none --data-cache=none --image-optimization=noneChange the options if your application needs them:
Your application uses Option ISR, revalidatePath,revalidateTag, or"use cache"--cdn-cache=response-store --response-store-mode=self-containednext/imagewith raster or remote images--image-optimization=cloudflare-imagesWorkers Response Store caches both rendered responses and data, so keep
--data-cache=nonewhen you use it. In self-contained mode, your application Worker owns the R2 bucket and Durable Object that the cache uses. To run the cache in a separate Worker, use--response-store-mode=service-bindinginstead, and deploy the cache Worker with thedeploy:response-storescript before you deploy your application. For more information, refer to Caching in vinext ↗︎.vinext initadds dependencies,vite.config.ts,cloudflare.config.ts, and thedev:vinext,build:vinext,start:vinext, anddeploy:vinextscripts. It can also upgradereactandreact-dom.You do not need
nodejs_compatincompatibilityFlags. A compatibility date of2026-08-04or later enables Node.js compatibility by default. -
Add
.cloudflare/typesto theincludearray intsconfig.json. Add.cloudflare/**,.vinext/**, anddist/**to your ESLint ignores. -
Replace Vercel-specific packages. Declare bindings in
cloudflare.config.ts, and read them in Server Components, Route Handlers, and Server Actions:import { env } from "cloudflare:workers"; -
Start the vinext development server:
npm run dev:vinextyarn run dev:vinextpnpm run dev:vinext -
Build your application and run the built Worker locally:
npm run build:vinextyarn run build:vinextpnpm run build:vinextnpm run start:vinextyarn run start:vinextpnpm run start:vinext -
Log in with the Cloudflare CLI and deploy your application:
npx cf auth loginyarn dlx cf auth loginpnpx cf auth loginnpx @vinext/cloudflare deployyarn dlx @vinext/cloudflare deploypnpx @vinext/cloudflare deploy
These Vercel and Next.js features behave differently on vinext:
| Feature | Behavior on vinext |
|---|---|
ISR, revalidatePath, and revalidateTag |
Supported. Choose a persistent cache when you run vinext init. The default cache is in memory. |
next/font/google |
Supported. Fonts are downloaded at build time and served from your Worker. |
next/image |
Without an optimizer, /_next/image redirects to the original file. |
runtime and preferredRegion route options |
Ignored. Every route runs on Workers. |
vercel.json |
Not read. Move its rules into next.config. |
@vercel/kv, @vercel/blob, @vercel/postgres |
Not supported. Use Workers KV, R2, and Hyperdrive or D1. |
vinext initexits withvinext init needs Cloudflare cache and image choices. Coding agents run without an interactive terminal. Pass--cdn-cache,--data-cache, and--image-optimization.- The Worker uses the wrong name. Pass
--nametovinext init, or editworker.nameincloudflare.config.ts. The--nameoption of@vinext/cloudflare deploydoes not apply tocloudflare.config.tsprojects. - ESLint reports thousands of problems after the first build. ESLint lints the
.cloudflare/build output. Add the build directories to your ESLint ignores. - Deploy prompts for an account. Set the
CLOUDFLARE_ACCOUNT_IDenvironment variable, or addaccountIdtocloudflare.config.ts.