Why deploying Next.js is still hard

Next.js remains the default choice for React applications, and its developer experience is genuinely strong. But getting a Next.js app running on serverless platforms like Cloudflare, Netlify, or AWS Lambda means reshaping its bespoke build output into something each platform can execute. OpenNext was built to solve exactly that problem, and it works — up to a point. Reverse-engineering Next.js's build output version after version turns into a fragile game of whack-a-mole.

Next.js has started work on a first-class adapters API, and we've been collaborating on it. But adapters only address build and deploy. During development, next dev runs exclusively in Node.js with no way to plug in a different runtime. If your app relies on platform-specific services like Durable Objects, KV, or AI bindings, you can't exercise that code in dev without workarounds.

A reimplementation, not a wrapper

BLOG-3194 1

What if, instead of adapting Next.js output, you reimplemented the Next.js API surface directly on Vite? Not a wrapper or adapter, but a clean reimplementation: routing, server rendering, React Server Components, server actions, caching, middleware — all built as a Vite plugin. This is the bet behind vinext (pronounced "vee-next"), an experimental drop-in replacement for Next.js built by one engineer and an AI model in under a week, at a cost of roughly $1,100 in tokens.

Vite is the build tool underlying most of the non-Next.js front-end ecosystem, powering Astro, SvelteKit, Nuxt, and Remix. Crucially, Vite output runs anywhere thanks to the Vite Environment API.

BLOG-3194 2

The migration path is intentionally frictionless: replace next with vinext in your scripts and keep your existing app/, pages/, and next.config.js.

npm install vinext

Early benchmark numbers

The initial benchmarks compare vinext against Next.js 16 using a shared 33-route App Router application. To isolate bundler and compilation speed, the Next.js build runs with TypeScript type checking and ESLint disabled (Vite doesn't run these during builds), and uses force-dynamic to skip static pre-rendering. Benchmarks run on GitHub CI on every merge to main.

vinext dev          # Development server with HMR
vinext build        # Production build
vinext deploy       # Build and deploy to Cloudflare Workers

Client bundle size (gzipped):

Framework Mean vs Next.js
Next.js 16.1.6 (Turbopack) 7.38s baseline
vinext (Vite 7 / Rollup) 4.64s 1.6x faster
vinext (Vite 8 / Rolldown) 1.67s 4.4x faster

These numbers measure build-time performance, not production serving speed, and the fixture is a single 33-route app, not a representative sample. The full methodology and historical results are public; treat these as directional. Still, Vite's architecture — especially Rolldown, the Rust-based bundler coming in Vite 8 — shows structural advantages that show up clearly here.

Deploying to Cloudflare Workers

Cloudflare Workers is vinext's first deployment target, and a single command takes you from source to a running Worker:

Framework Gzipped vs Next.js
Next.js 16.1.6 168.9 KB baseline
vinext (Rollup) 74.0 KB 56% smaller
vinext (Rolldown) 72.9 KB 57% smaller

That one command builds the app, auto-generates the Worker configuration, and deploys. Both the App Router and Pages Router work on Workers, with full client-side hydration, interactive components, client-side navigation, and React state.

For production caching, vinext ships with a Cloudflare KV cache handler that provides Incremental Static Regeneration (ISR) out of the box:

vinext deploy

The caching layer is pluggable — swap in R2 via that setCacheHandler call for large payloads or different access patterns. Live examples running today include an App Router playground, a Hacker News clone, and minimal App and Pages Router examples. There's also a working demo of Cloudflare Agents running inside a Next.js app without needing something like getPlatformProxy, since the entire app now runs in workerd during both dev and deploy.

Current status and coverage

To be clear, vinext is experimental. It's less than a week old and hasn't seen meaningful production traffic at scale. But the test suite is substantial: over 1,700 Vitest tests and 380 Playwright E2E tests, including tests ported from Next.js's own suite and OpenNext's Cloudflare conformance suite. Coverage sits at 94% of the Next.js 16 API surface.

Real-world feedback is already coming in. National Design Studio, a team modernizing government interfaces, is running vinext in production on CIO.gov, reporting meaningful improvements in build times and bundle sizes. The README is upfront about unsupported features and known limitations.

One notable gap: vinext doesn't yet support static pre-rendering at build time. ISR works today — pages get cached after the first request and revalidated in the background, just like Next.js. But there's no build-time equivalent of generateStaticParams() yet. That's an intentional design decision, and it's on the roadmap. For purely static sites, you'd be better served by a Vite-based framework like Astro.

Traffic-aware pre-rendering

The omission of build-time pre-rendering isn't just a missing feature — it's a philosophical shift. Next.js pre-renders every page listed in generateStaticParams() during the build. A site with 10,000 product pages means 10,000 renders at build time, even if 99% of those pages never receive a request. Builds scale linearly with page count, which is why large Next.js sites end up with 30-minute builds.

Instead, vinext introduces Traffic-aware Pre-Rendering (TPR), currently experimental. Since Cloudflare is already the reverse proxy for your site, vinext can query zone analytics at deploy time and pre-render only the pages that actually get visited.

import { KVCacheHandler } from "vinext/cloudflare";
import { setCacheHandler } from "next/cache";

setCacheHandler(new KVCacheHandler(env.MY_KV_NAMESPACE));

For a site with 100,000 product pages, the power law usually means 90% of traffic goes to 50-200 pages. Those get pre-rendered in seconds. Everything else falls back to on-demand SSR and gets cached via ISR after the first request. Each new deploy refreshes the set based on current traffic — pages that go viral get picked up automatically, without coupling your build to your production database.

What AI changed

A project like this would normally take a team of engineers months, if not years. Several companies have attempted it and come up short — Cloudflare tried once before. Two routers, 30+ module shims, server rendering pipelines, RSC streaming, file-system routing, middleware, caching: there's a reason nobody had pulled it off.

This time it took under a week, with one engineer directing an AI model. The first commit landed on February 13. By the end of that evening, both the Pages Router and App Router had basic SSR working alongside middleware, server actions, and streaming. The next afternoon, the App Router Playground was rendering 10 of 11 routes. By day three, vinext deploy was shipping apps to Cloudflare Workers with full client hydration. The rest of the week went to hardening: fixing edge cases, expanding the test suite, and pushing API coverage to 94%.

What changed from earlier attempts? AI got better — way better.

Why Next.js was the right candidate

This approach only works when several conditions align, and they happened to align here. Next.js itself is one of them. Its API surface — getServerSideProps, useRouter, and the rest — is thoroughly documented and widely discussed across years of tutorials, forum posts, and production code. That means an AI model trained on public code has seen it all before and tends to produce accurate implementations rather than confident hallucinations.

The project also benefited from Next.js's extensive test suite, which includes thousands of end-to-end tests covering individual features and edge cases. Porting those tests directly from the Next.js repository gave us a mechanical specification to verify against. Combined with Vite as a foundation — with its fast HMR, native ESM support, and clean plugin API — we didn't need to build a bundler. The early-stage @vitejs/plugin-rsc provided React Server Components support without requiring us to implement RSC from scratch.

Perhaps most importantly, the AI models were finally capable enough. Earlier models could not sustain coherence across a codebase of this size. Current models can hold the full architecture in context, reason about module interactions, and even dig into Next.js, Vite, and React internals to debug issues. The state of the art has improved to the point where this kind of project is feasible.

The build process

Nearly every line of code in vinext was written by AI, but every line passed the same quality gates as human-written code. The project has over 1,700 Vitest tests, 380 Playwright E2E tests, full TypeScript checking via tsgo, and linting via oxlint, all running in CI on every pull request. Those guardrails were essential to making AI productive rather than chaotic.

The workflow started with a plan. A few hours of back-and-forth with Claude in OpenCode defined the architecture, the build order, and the abstractions to use. From there, the process was repetitive:

  1. Define a task, such as implementing the next/navigation shim with usePathname, useSearchParams, and useRouter.
  2. Let the AI write the implementation and tests.
  3. Run the test suite.
  4. Merge if tests pass; otherwise feed the error output back to the AI and iterate.
  5. Repeat.

Code review was also partially automated. An AI agent reviewed pull requests, and another agent addressed the comments. The feedback loop ran largely without human intervention.

It was not flawless. Some pull requests were simply wrong — implementations that seemed correct but did not match actual Next.js behavior. Human course-correction was needed regularly. Architecture decisions, prioritization, and recognizing dead ends remained my responsibility. AI works well with good direction, context, and guardrails, but a human still has to steer.

For browser-level verification, agent-browser checked actual rendered output, client-side navigation, and hydration behavior — things unit tests routinely miss. Across the project, over 800 OpenCode sessions ran, totaling roughly $1,100 in Claude API tokens.

Implications for the software stack

The project raises a question about why so many layers exist in modern software. Most abstractions were created because humans cannot hold entire systems in their heads; layers exist to manage complexity and make the next person's job easier. That is how frameworks accumulate on top of frameworks, along with wrapper libraries and glue code.

AI does not share that limitation. It can keep a full system in context and produce the code directly, without needing an intermediate framework for organization. All it requires is a specification and a foundation to build on.

It remains unclear which abstractions are truly foundational and which were merely cognitive crutches. That distinction will shift considerably in the coming years. vinext is one data point: given an API contract, a build tool, and an AI model, the AI wrote everything in between — no intermediate framework required. That pattern will likely repeat across other areas of software, and not all existing layers will survive.

Migrating an existing project

vinext ships with an Agent Skill that automates migration for Claude Code, OpenCode, Cursor, Codex, and other AI coding tools. After installing it, opening a Next.js project and requesting a migration triggers the skill to check compatibility, install dependencies, generate config, and start the dev server. It understands vinext's capabilities and flags anything requiring manual attention.

npx skills add cloudflare/vinext

To migrate, open your Next.js project in a supported tool and state the intent:

migrate this project to vinext

Manual migration is also possible:

npx vinext init    # Migrate an existing Next.js project
npx vinext dev     # Start the dev server
npx vinext deploy  # Ship to Cloudflare Workers

The source code is available at github.com/cloudflare/vinext. Issues, pull requests, and feedback are welcome.