| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Run Next.js applications on Vite, with Cloudflare Workers as the primary deployment target.
Website: vinext.dev
Read the announcement: How we rebuilt Next.js with AI in one week
Under active development. vinext supports substantial Next.js applications today, but it is not yet a drop-in replacement for every application or production workload. Expect compatibility gaps, especially in newer App Router features, and evaluate it against your own application before adopting it.
vinext reimplements the Next.js API surface on Vite rather than consuming next build output. It supports both the App Router and Pages Router, React Server Components, Server Actions, middleware, route handlers, ISR, static export, and the most commonly used next/* modules. Cloudflare Workers has the deepest integration; Node.js and other platforms are available with different levels of support.
These are active compatibility areas, not permanent exclusions:
Run vinext check against an existing application before migrating. If a gap is not listed here, check the open issues or file a focused reproduction.
Use the official setup commands below. They are the recommended way to create or migrate a vinext project because they configure dependencies, scripts, Vite, and your deployment target for you.
Start a new project with create-vinext-app:
pnpm create vinext-app@latest my-appMigrate an existing Next.js project with vinext init:
npx vinext initPrefer vinext init for a direct, repeatable migration. If you want an AI agent to investigate compatibility issues and guide the migration, vinext also includes an optional Agent Skill. It works with Claude Code, OpenCode, Cursor, Codex, and dozens of other AI coding tools:
npx skills add cloudflare/vinextThen open your Next.js project in any supported tool and say:
migrate this project to vinext
The skill handles compatibility checking, dependency installation, config generation, and dev server startup. It knows what vinext supports and will flag anything that needs manual attention.
npm install vinext
npm install -D vite @vitejs/plugin-reactIf you're using the App Router, also install:
npm install react-server-dom-webpack
npm install -D @vitejs/plugin-rscReplace next with vinext in your scripts:
{
"scripts": {
"dev": "vinext dev",
"build": "vinext build",
"start": "vinext start"
}
}vinext dev # Development server with HMR
vinext build # Production build
npx @vinext/cloudflare deploy # Build and deploy to Cloudflare WorkersWith Vite+, use vpx @vinext/cloudflare deploy, or vp exec vinext-cloudflare deploy when running the locally installed bin.
vinext auto-detects your app/ or pages/ directory, loads next.config.js, and configures Vite automatically. No vite.config.ts required for basic usage.
Your existing pages/, app/, next.config.js, and public/ directories work as-is. Run vinext check first to scan for known compatibility issues, or use vinext init to automate the full migration.
| Command | Description |
|---|---|
| vinext dev | Start dev server with HMR |
| vinext build | Production build (multi-environment for App Router: RSC + SSR + client) |
| vinext start | Start local production server for testing |
| npx @vinext/cloudflare deploy | Build and deploy to Cloudflare Workers |
| vp exec vinext-cloudflare deploy | Build and deploy to Cloudflare Workers with Vite+ |
| vinext init | Migrate a Next.js project to run under vinext |
| vinext check | Scan your Next.js app for compatibility issues before migrating |
| vinext lint | Delegate to eslint or oxlint |
Options: -p / --port <port>, -H / --hostname <host>, --turbopack (accepted, no-op).
@vinext/cloudflare deploy options: --preview, --env <name>, --name <name>, --skip-build, --dry-run, --experimental-tpr.
vinext init prompts for a deployment target, defaulting to Cloudflare. Agents must ask the user which target they want, then pass --platform=cloudflare or --platform=node.
Other options: --port <port> (default: 3001), --skip-check, --force.
If your next.config.* sets output: "standalone", vinext build emits a self-hosting bundle at dist/standalone/. Start it with:
node dist/standalone/server.jsEnvironment variables: PORT (default 3000), HOST (default 0.0.0.0).
Note: Next.js standalone uses HOSTNAME for the bind address, but vinext uses HOST to avoid collision with the system-set HOSTNAME variable on Linux. Update your deployment config accordingly.
Use create-vinext-app for new projects. It creates a TypeScript App Router project with Tailwind CSS and then runs the same vinext init setup used for existing apps:
pnpm create vinext-app@latest my-appThe generated project is Cloudflare Workers-ready by default. Pass --platform=node if you want the Node target instead.
vinext init automates the migration in one command:
npx vinext initThis will:
The migration is non-destructive -- your existing Next.js setup continues to work alongside vinext. It does not modify next.config, tsconfig.json, or any source files, and it does not remove Next.js dependencies.
vinext targets Vite 8, which defaults to Rolldown, Oxc, Lightning CSS, and a newer browser baseline. If you bring custom Vite config or plugins from an older setup, prefer oxc, optimizeDeps.rolldownOptions, and build.rolldownOptions over older esbuild and build.rollupOptions knobs, and override build.target if you still need older browsers. If a dependency breaks because of stricter CommonJS default import handling, fix the import or use legacy.inconsistentCjsInterop: true as a temporary escape hatch. See the Vite 8 migration guide.
npm run dev:vinext # Start the vinext dev server (port 3001)
npm run build:vinext # Build production output with vinext
npm run start:vinext # Start vinext production server
npm run dev # Still runs Next.js as beforeUse --platform=cloudflare or --platform=node to skip the platform prompt. Cloudflare init updates an existing JavaScript or TypeScript Vite config using its AST, preserving unrelated settings. Use --force to replace an existing Node-target Vite config, or --skip-check to skip the compatibility report.
Vite has become the default build tool for modern web frameworks — fast HMR, a clean plugin API, native ESM, and a growing ecosystem. With @vitejs/plugin-rsc adding React Server Components support, it's now possible to build a full RSC framework on Vite.
vinext reimplements the Next.js API surface on Vite so existing Next.js applications can run on a different toolchain. The answer, so far, is that substantial applications can.
vinext works everywhere. It natively supports Cloudflare Workers (with npx @vinext/cloudflare deploy or vp exec vinext-cloudflare deploy, bindings, KV caching), and can be deployed to Vercel, Netlify, AWS, Deno Deploy, and more via the Nitro Vite plugin. Native support for additional platforms is planned.
Alternatives worth knowing about:
What is this? vinext is a Vite plugin that reimplements the public Next.js API — routing, server rendering, next/* module imports, the CLI — so you can run Next.js applications on Vite instead of the Next.js compiler toolchain. It can be deployed anywhere: Cloudflare Workers is the first natively supported target, with other platforms available via Nitro. Native adapters for more platforms are planned.
Is this a fork of Next.js? No. vinext is an alternative implementation of the Next.js API surface built on Vite. The core is written from scratch. The goal is not to create a competing framework or add features beyond what Next.js offers; it is to provide the same well-defined API surface on Vite's toolchain.
Does vinext require Next.js to be installed? No. vinext ships fallback declarations for the supported next and next/* APIs, so applications can run and type-check without the next package. If both packages are installed, vinext keeps using Next.js's authoritative types and adds only its own extensions. Compatibility features that consume Next.js internals, such as styled-jsx, may still require a matching Next.js installation when used.
How is this different from OpenNext? OpenNext adapts the output of a standard next build to run on various platforms. Because it builds on Next.js's own output, it inherits broad API coverage and has been well-tested for much longer. vinext takes a different approach: it reimplements the Next.js APIs on Vite from scratch, which means faster builds and smaller bundles, but less coverage of the long tail of Next.js features. If you need a mature, well-tested way to run Next.js outside Vercel, OpenNext is the safer choice. If you want a lighter Vite-based toolchain and do not need every Next.js API, vinext may be a good fit.
Can I use this in production? You can, with caution. vinext has known compatibility gaps and has not yet been battle-tested across the full range of production Next.js workloads. Evaluate the features and deployment target your application relies on before adopting it.
Can I just self-host Next.js? Yes. Next.js supports self-hosting on Node.js servers, Docker containers, and static exports. If you're happy with the Next.js toolchain and just want to run it somewhere other than Vercel, self-hosting is the simplest path.
How are you verifying this works? The test suite has over 1,700 Vitest tests and 380 Playwright E2E tests. This includes tests ported directly from the Next.js test suite and OpenNext's Cloudflare conformance suite, covering routing, SSR, RSC, server actions, caching, metadata, middleware, streaming, and more. Vercel's App Router Playground also runs on vinext as an integration test. See the Tests section and tests/nextjs-compat/TRACKING.md for details.
Who is reviewing this code? A mix of humans and AI agents. Humans review PRs before they merge, focused on behavior, structure, and long-term direction. We lean heavily on agent-driven code review to catch issues at PR time and across the codebase. The test suite is the primary quality gate. Outside contributions and deeper human code review are very welcome.
Why Vite? Vite is an excellent build tool with a rich plugin ecosystem, first-class ESM support, and fast HMR. The @vitejs/plugin-rsc plugin adds React Server Components support with multi-environment builds. vinext builds the Next.js developer experience on top of that infrastructure.
Does this support the Pages Router, App Router, or both? Both. File-system routing, SSR, client hydration, and deployment to Cloudflare Workers work for both routers.
What version of Next.js does this target? Next.js 16.x. No support for deprecated APIs from older versions.
Can I deploy to AWS/Netlify/other platforms? Yes. Add the Nitro Vite plugin alongside vinext, and you can deploy to Vercel, Netlify, AWS Amplify, Deno Deploy, Azure, and many more. See Other platforms (via Nitro) for setup. For Cloudflare Workers, the native integration (npx @vinext/cloudflare deploy or vp exec vinext-cloudflare deploy) gives you the smoothest experience. Native adapters for more platforms are planned.
What happens when Next.js releases a new feature? We track the public Next.js API surface and add support for new stable features. Experimental or unstable Next.js features are lower priority. The plan is to add commit-level tracking of the Next.js repo so we can stay current as new versions are released.
vinext has native integration with Cloudflare Workers through @cloudflare/vite-plugin, including bindings access via cloudflare:workers, KV caching, image optimization, and the @vinext/cloudflare deploy one-command workflow.
Before running npx @vinext/cloudflare deploy for the first time you need to authenticate with Cloudflare and tell wrangler which account to deploy to.
Authentication — pick one:
Account ID:
wrangler needs to know which Cloudflare account to deploy to. Add your account ID to wrangler.jsonc:
Find your account ID in the Cloudflare dashboard URL (dash.cloudflare.com/<account-id>) or by running wrangler whoami after logging in.
Alternatively, set the CLOUDFLARE_ACCOUNT_ID environment variable instead of hardcoding it in the config file.
Run vinext init --platform=cloudflare first to install dependencies and create or AST-update vite.config.* and wrangler.jsonc. @vinext/cloudflare deploy then validates that setup, builds the application, and deploys to Workers without rewriting project configuration.
Cloudflare init can also configure image optimization declaratively in the Vite config with imagesOptimizer() and add the matching Wrangler Images binding. The built-in fetch handlers register that optimizer at runtime; image optimization is not implemented or generated by @vinext/cloudflare deploy.
npx @vinext/cloudflare deploy
vp exec vinext-cloudflare deploy
npx @vinext/cloudflare deploy --env staging
vp exec vinext-cloudflare deploy --env stagingUse --env <name> to target wrangler.jsonc env.<name>. --preview is shorthand for --env preview.
The init command also auto-detects and fixes common migration issues:
Both App Router and Pages Router work on Workers with full client-side hydration.
Use import { env } from "cloudflare:workers" to access bindings in any server component, route handler, or server action. No custom worker entry or special configuration required.
import { env } from "cloudflare:workers";
export default async function Page() {
const result = await env.DB.prepare("SELECT * FROM posts").all();
return <div>{JSON.stringify(result)}</div>;
}This works because @cloudflare/vite-plugin runs the RSC environment in workerd, where cloudflare:workers is a native module. In production builds, the import is externalized so workerd resolves it at runtime. All binding types are supported: D1, R2, KV, Durable Objects, AI, Queues, Vectorize, Browser Rendering, etc.
Define your bindings in wrangler.jsonc as usual:
{
"name": "my-app",
"compatibility_date": "2026-02-12",
"compatibility_flags": ["nodejs_compat"],
"d1_databases": [{ "binding": "DB", "database_name": "my-db", "database_id": "..." }],
"kv_namespaces": [{ "binding": "CACHE", "id": "..." }],
}For TypeScript types, generate them with wrangler types and the env import will be fully typed.
Note: You do not need getPlatformProxy(), a custom worker entry with fetch(request, env), or any other workaround. cloudflare:workers is the recommended way to access bindings in vinext.
TPR queries Cloudflare zone analytics at deploy time to find which pages actually get traffic, pre-renders only those, and uploads them to KV cache. The result is SSG-level latency for popular pages without pre-rendering your entire site.
npx @vinext/cloudflare deploy --experimental-tpr # Pre-render pages covering 90% of traffic
vp exec vinext-cloudflare deploy --experimental-tpr # Same, with Vite+
npx @vinext/cloudflare deploy --experimental-tpr --tpr-coverage 95 # More aggressive coverage
npx @vinext/cloudflare deploy --experimental-tpr --tpr-limit 500 # Cap at 500 pages
npx @vinext/cloudflare deploy --experimental-tpr --tpr-window 48 # Use 48h of analyticsRequires a custom domain (zone analytics are unavailable on *.workers.dev) and CLOUDFLARE_API_TOKEN with Zone.Analytics read permission.
If you need to customize the Vite config, create a vite.config.ts. vinext will merge its config with yours. For Cloudflare Workers deployment with the App Router, configure @cloudflare/vite-plugin so the RSC environment runs in workerd:
import { defineConfig } from "vite";
import vinext from "vinext";
import { cloudflare } from "@cloudflare/vite-plugin";
export default defineConfig({
plugins: [
vinext(),
cloudflare({
viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] },
}),
],
});Do not register @vitejs/plugin-rsc yourself. It is an optional peer dependency, so it must be installed in your project, but vinext auto-registers it whenever an app/ directory is detected. Adding an explicit rsc() call fails the build with [vinext] Duplicate @vitejs/plugin-rsc detected. Pass rsc: false to vinext() only if you want to own that registration.
For client-side Module Federation, configure React and React DOM as singleton shared modules in both the host and remotes:
import { federation } from "@module-federation/vite";
import { defineConfig } from "vite";
import vinext from "vinext";
export default defineConfig({
plugins: [
federation({
name: "host",
shared: {
react: { singleton: true },
"react/": { singleton: true },
"react-dom": { singleton: true },
"react-dom/": { singleton: true },
},
}),
vinext(),
],
});In a remote client component, use getVinextReact() before reading React hooks. vinext registers the host's browser React instance before application modules execute, and the first registration remains stable across remote evaluation and HMR:
"use client";
import * as React from "react";
import { getVinextReact } from "vinext/client";
const { useState } = getVinextReact(React);
export function RemoteCounter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount((value) => value + 1)}>{count}</button>;
}This bridge is browser-only. It does not provide App Router Module Federation SSR or transparently replace React imports inside third-party packages; compatible React versions remain the responsibility of the Module Federation shared configuration.
See the examples for complete working configurations.
For deploying to platforms other than Cloudflare, vinext works with Nitro as a Vite plugin. Add nitro alongside vinext in your Vite config and deploy to any Nitro-supported platform.
import { defineConfig } from "vite";
import vinext from "vinext";
import { nitro } from "nitro/vite";
export default defineConfig({
plugins: [vinext(), nitro()],
});npm install nitroNitro auto-detects the deployment platform in most CI/CD environments (Vercel, Netlify, AWS Amplify, Azure, and others), so you typically don't need to set a preset. For local builds, set the NITRO_PRESET environment variable:
NITRO_PRESET=vercel npx vite build
NITRO_PRESET=netlify npx vite build
NITRO_PRESET=deno_deploy npx vite buildVercelDeploying to Cloudflare? You can use Nitro, but the native integration (npx @vinext/cloudflare deploy, vp exec vinext-cloudflare deploy, and @cloudflare/vite-plugin) is recommended. It provides the best developer experience with cloudflare:workers bindings, KV caching, image optimization, and one-command deploys.
Nitro auto-detects Vercel in CI. For local builds:
NITRO_PRESET=vercel npx vite buildDeploy with the Vercel CLI or connect your Git repo in the Vercel dashboard. Set the build command to vite build and the output directory to .output.
NetlifyNitro auto-detects Netlify in CI. For local builds:
NITRO_PRESET=netlify npx vite buildDeploy with the Netlify CLI or connect your Git repo. Set the build command to vite build.
AWS (Amplify)Nitro auto-detects AWS Amplify in CI. For local builds:
NITRO_PRESET=aws_amplify npx vite buildConnect your Git repo in the AWS Amplify console. Set the build command to vite build.
Deno DeployNITRO_PRESET=deno_deploy npx vite build
cd .output
deployctl deploy --project=my-project server/index.tsNITRO_PRESET=node npx vite build
node .output/server/index.mjsThis produces a standalone Node.js server. Suitable for Docker, VMs, or any environment that can run Node.
See the Nitro deployment docs for the full list of supported platforms and provider-specific configuration.
These are deployed to Cloudflare Workers and updated on every push to main:
| Example | Description | URL |
|---|---|---|
| App Router Playground | Vercel's Next.js App Router Playground running on vinext | app-router-playground.vinext.workers.dev |
| Hacker News | HN clone (App Router, RSC) | hackernews.vinext.workers.dev |
| Nextra Docs | Nextra docs site (MDX, App Router) | nextra-docs-template.vinext.workers.dev |
| App Router (minimal) | Minimal App Router on Workers | app-router-cloudflare.vinext.workers.dev |
| Pages Router (minimal) | Minimal Pages Router on Workers | pages-router-cloudflare.vinext.workers.dev |
| RealWorld API | REST API routes example | realworld-api-rest.vinext.workers.dev |
| Benchmarks Dashboard | Build performance tracking over time (D1-backed) | vinext.dev/benchmarks |
| App Router + Nitro | App Router deployed via Nitro (multi-platform) | examples/app-router-nitro |
~94% of the Next.js 16 API surface has full or partial support. The remaining gaps are intentional stubs for deprecated features, plus Partial Prerendering and Cache Components. Next.js 16 reworked PPR into "use cache"; vinext implements that directive for file-level and function-level caching, but full cacheComponents behavior is still incomplete — see Known gaps we're working on.
✅ = full implementation | 🟡 = partial (runtime behavior correct, some build-time optimizations missing) | ⬜ = intentional stub/no-op
Every next/* import is shimmed to a Vite-compatible implementation.
| Module | Notes | |
|---|---|---|
| next/link | ✅ | All props including prefetch (IntersectionObserver), onNavigate, scroll restoration, basePath, locale |
| next/image | 🟡 | Remote images via @unpic/react (28 CDNs). Local images via <img> + srcSet. No build-time optimization/resizing |
| next/head | ✅ | SSR collection + client-side DOM manipulation |
| next/router | ✅ | useRouter, Router singleton, events, client-side navigation, SSR context, i18n |
| next/navigation | ✅ | usePathname, useSearchParams, useParams, useRouter, redirect, notFound, forbidden, unauthorized |
| next/server | ✅ | NextRequest, NextResponse, NextURL, cookies, userAgent, after, connection, URLPattern |
| next/headers | ✅ | Async headers(), cookies(), draftMode() |
| next/dynamic | ✅ | ssr: true, ssr: false, loading component |
| next/script | ✅ | All 4 strategies (beforeInteractive, afterInteractive, lazyOnload, worker) |
| next/font/google | 🟡 | Runtime CDN loading. No self-hosting, font subsetting, or fallback metrics |
| next/font/local | 🟡 | Runtime @font-face injection. Not extracted at build time |
| next/og | ✅ | OG image generation via @vercel/og (Satori + resvg) |
| next/cache | ✅ | revalidateTag, revalidatePath, unstable_cache, pluggable CacheHandler, "use cache" with cacheLife() and cacheTag() |
| next/form | ✅ | GET form interception + POST server action delegation |
| next/legacy/image | ✅ | Translates legacy props to modern Image |
| next/error | ✅ | Default error page component |
| next/config | ✅ | getConfig / setConfig |
| next/document | ✅ | Html, Head, Main, NextScript |
| next/constants | ✅ | All phase constants |
| next/amp | ⬜ | No-op (AMP is deprecated) |
| next/web-vitals | ⬜ | No-op (use the web-vitals library directly) |
| Feature | Notes | |
|---|---|---|
| File-system routing (pages/) | ✅ | Automatic scanning with hot-reload on file changes |
| File-system routing (app/) | ✅ | Pages, routes, layouts, templates, loading, error, not-found, forbidden, unauthorized |
| Dynamic routes [param] | ✅ | Both routers |
| Catch-all [...slug] | ✅ | Both routers |
| Optional catch-all [[...slug]] | ✅ | Both routers |
| Route groups (group) | ✅ | URL-transparent, layouts still apply |
| Parallel routes @slot | ✅ | Discovery, layout props, default.tsx, inherited slots |
| Intercepting routes | ✅ | (.), (..), (..)(..), (...) conventions |
| Route handlers (route.ts) | ✅ | Named HTTP methods, auto OPTIONS/HEAD, cookie attachment |
| Middleware | ✅ | middleware.ts and proxy.ts (Next.js 16). Matcher patterns (string, array, regex, :param, :path*, :path+) |
| i18n routing | 🟡 | Pages Router locale prefix, Accept-Language detection, NEXT_LOCALE cookie. No domain-based routing |
| basePath | ✅ | Applied everywhere — URLs, Link, Router, navigation hooks |
| trailingSlash | ✅ | 308 redirects to canonical form |
| Feature | Notes | |
|---|---|---|
| SSR (Pages Router) | ✅ | Streaming, _app/_document, __NEXT_DATA__, hydration |
| SSR (App Router) | ✅ | RSC pipeline, nested layouts, streaming, nav context for client components |
| getStaticProps | ✅ | Props, redirect, notFound, revalidate |
| getStaticPaths | ✅ | fallback: false, true, "blocking" |
| getServerSideProps | ✅ | Full context including locale |
| ISR | ✅ | Stale-while-revalidate, pluggable CacheHandler, background regeneration |
| Server Actions ("use server") | ✅ | Action execution, FormData, re-render after mutation, redirect() in actions |
| React Server Components | ✅ | Via @vitejs/plugin-rsc. "use client" boundaries work correctly |
| Streaming SSR | ✅ | Both routers |
| Metadata API | ✅ | metadata, generateMetadata, viewport, generateViewport, title templates |
| generateStaticParams | ✅ | With dynamicParams enforcement |
| Metadata file routes | ✅ | sitemap.xml, robots.txt, manifest, favicon, OG images (static + dynamic) |
| Static export (output: 'export') | ✅ | Generates static HTML/JSON for all routes |
| Standalone output (output: 'standalone') | ✅ | Generates dist/standalone with server.js, build artifacts, and runtime deps |
| connection() | ✅ | Forces dynamic rendering |
| "use cache" directive | ✅ | File-level and function-level. cacheLife() profiles, cacheTag(), stale-while-revalidate |
| instrumentation.ts | ✅ | register() and onRequestError() callbacks |
| Route segment config | 🟡 | revalidate, dynamic, dynamicParams. runtime and preferredRegion are ignored |
| Feature | Notes | |
|---|---|---|
| next.config.js / .ts / .mjs | ✅ | Function configs, phase argument |
| rewrites / redirects / headers | ✅ | All phases, param interpolation |
| Environment variables (.env*, NEXT_PUBLIC_*) | ✅ | Auto-loads Next.js-style dotenv files; only public vars are inlined |
| images config | 🟡 | Parsed but not used for optimization |
| experimental.optimizePackageImports | ✅ | Rewrites barrel imports to direct sub-module imports in RSC/SSR environments. A default set (lucide-react, date-fns, radix-ui, antd, MUI, and others) are always optimized. Add package names here to extend the list. |
| vinext({ nextConfig }) | ✅ | Inline Next-style config from vite.config.*. Supports object-form and function-form config. When provided, this overrides root next.config.*. |
vinext automatically loads dotenv files for dev, build, start, and deploy.
Load order matches Next.js (highest priority first):
Modes:
Variable expansion ($VAR / ${VAR}) is supported.
Client exposure remains explicit:
Override behavior:
The cache is pluggable. The default MemoryCacheHandler works out of the box. Swap in your own backend for production.
Instead of wiring up cache handlers imperatively from a worker entry, you can declare them in the vinext() plugin config. The @vinext/cloudflare package ships Cloudflare adapters for this:
The two fill different slots and can be used together:
import { defineConfig } from "vite";
import vinext from "vinext";
import { cdnAdapter } from "@vinext/cloudflare/cache/cdn-adapter";
import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";
export default defineConfig({
plugins: [
vinext({
cache: {
cdn: cdnAdapter(),
data: kvDataAdapter(),
},
}),
],
});The KV data adapter reads env[binding] at runtime, so add the matching KV namespace to your wrangler.jsonc:
{
"kv_namespaces": [{ "binding": "VINEXT_KV_CACHE", "id": "<your-namespace-id>" }],
}binding defaults to VINEXT_KV_CACHE, so kvDataAdapter() with no options works as long as that's your binding name. Other options: appPrefix (namespace cache keys to isolate multiple apps in one KV namespace), ttlSeconds (default KV expirationTtl, default 30 days), and tagCacheTtlMs (in-memory tag-invalidation cache TTL, default 5s).
cdnAdapter() takes no options, but the Workers Cache only exposes ctx.cache when cache.enabled is set in wrangler.jsonc:
{
"cache": { "enabled": true },
}While the data adapter can store entries and serve HIT/STALE itself, the CDN adapter delegates serving to Cloudflare's edge: the origin renders fresh responses and tags them with Cache-Tag, and revalidateTag() / revalidatePath() purge the edge through ctx.cache.purge({ tags }). See examples/workers-cache for both adapters wired up together.
Each builder returns a plain, serializable { adapter, options } descriptor — it never touches the Workers runtime, so nothing throws at build or dev time when bindings aren't available. The actual adapter (and its env binding lookup) is instantiated lazily on the first request.
Registration is wired into every router and runtime — App Router and Pages Router, on Cloudflare Workers as well as the Node.js server (vinext start) and dev. It self-guards (instantiated once per isolate) and is resilient: if an adapter can't initialize on a given runtime (e.g. a KV binding doesn't exist on the Node server), vinext logs a warning and falls back to the default handler instead of failing requests.
To write your own adapter, point a slot at any module by path and default-export a factory that receives { env, options } at runtime and returns a data-cache CacheHandler (or a CDN adapter):
vinext({
cache: {
data: {
adapter: require.resolve("./my-adapter.js"),
options: {/* … */},
},
},
});These are intentional exclusions. For things that are missing today but on the roadmap, see Known gaps we're working on above.
Caveat: Benchmarks are hard to get right and these are early results. Take them as directional, not definitive.
These benchmarks measure compilation and bundling speed, not production serving performance. Next.js and vinext have fundamentally different default approaches: Next.js statically pre-renders pages at build time (making builds slower but production serving faster for static content), while vinext server-renders all pages on each request. To make the comparison apples-to-apples, the benchmark app uses export const dynamic = "force-dynamic" to disable Next.js static pre-rendering — both frameworks are doing the same work: compiling, bundling, and preparing server-rendered routes.
The benchmark app is a shared 33-route App Router application (server components, client components, dynamic routes, nested layouts, API routes) built identically by both tools. We compare Next.js (Turbopack) against vinext (Vite 8). Both Turbopack and Rolldown parallelize across cores, so results on machines with more cores may differ significantly.
We measure three things:
Benchmarks run on GitHub CI runners (2-core Ubuntu) on every merge to main. See the launch numbers in the announcement blog post and the latest results at vinext.dev/benchmarks.
Why the bundle size difference?Analysis of the build output shows two main factors:
Both frameworks ship the same app code and the same RSC client runtime (react-server-dom-webpack). The difference is in how much of React's internals survive tree-shaking and how much framework plumbing each tool adds.
Reproduce with node benchmarks/run.mjs --runs=5 --dev-runs=10. Exact framework versions are recorded in each result.
vinext is a Vite plugin that:
The result is a standard Vite application that happens to be API-compatible with Next.js.
Request → Vite dev server middleware → Route match → getServerSideProps/getStaticProps → renderToReadableStream(App + Page) → HTML with __NEXT_DATA__ → Client hydration
Request → RSC entry (Vite rsc environment) → Route match → Build layout/page tree → renderToReadableStream (RSC payload) → SSR entry (Vite ssr environment) → renderToReadableStream (HTML) → Client hydration from RSC stream
packages/vinext/
src/
index.ts # Main plugin — resolve aliases, config, virtual modules
cli.ts # vinext CLI (dev/build/start/deploy/init/check/lint)
check.ts # Compatibility scanner
deploy.ts # Cloudflare Workers deployment
init.ts # vinext init — one-command migration for Next.js apps
client/
entry.ts # Client-side hydration entry
routing/
pages-router.ts # Pages Router file-system scanner
app-router.ts # App Router file-system scanner
entries/
app-rsc-entry.ts # App Router RSC entry generator
app-ssr-entry.ts # App Router SSR entry generator
app-browser-entry.ts # App Router browser entry generator
pages-server-entry.ts # Pages Router SSR entry generator
pages-client-entry.ts # Pages Router client entry generator
server/
dev-server.ts # Pages Router SSR request handler
prod-server.ts # Production server with compression
api-handler.ts # Pages Router API routes
isr-cache.ts # ISR cache layer
middleware.ts # middleware.ts / proxy.ts runner
metadata-routes.ts # File-based metadata route scanner
instrumentation.ts # instrumentation.ts support
shims/ # One file per next/* module (33 shims + 6 internal)
build/
static-export.ts # output: 'export' support
utils/
project.ts # Shared project utilities (ESM, CJS, package manager detection)
config/
next-config.ts # next.config.js loader
config-matchers.ts # Config matching utilities
tests/
*.test.ts # Vitest unit + integration tests
nextjs-compat/ # Tests ported from Next.js test suite
fixtures/ # Test apps (pages-basic, app-basic, ecosystem libs)
e2e/ # Playwright E2E tests (5 projects)
examples/ # Deployed demo apps (see Live Examples above)
pnpm test # Vitest unit + integration tests
pnpm run test:e2e # Playwright E2E tests (5 projects)
pnpm run check # Format, lint, and type checks
pnpm run lint # Lint only (type-aware oxlint)
pnpm run fmt # Formatting (oxfmt)
pnpm run fmt:check # Check formatting without writingE2E tests cover Pages Router (dev + production), App Router (dev), and both routers on Cloudflare Workers via wrangler dev.
The Vercel App Router Playground runs on vinext as an integration test — see it live at app-router-playground.vinext.workers.dev.
If you're working from the repo instead of installing from npm:
git clone https://github.com/cloudflare/vinext.git
cd vinext
pnpm install
pnpm run buildThis builds the vinext package to packages/vinext/dist/. For active development, use pnpm --filter vinext run dev to rebuild on changes.
To use it against an external Next.js app, link the built package:
# From your Next.js project directory:
pnpm link /path/to/vinext/packages/vinextOr add it to your package.json as a file dependency:
{
"dependencies": {
"vinext": "file:/path/to/vinext/packages/vinext"
}
}vinext has peer dependencies on react ^19.2.6, react-dom ^19.2.6, react-server-dom-webpack ^19.2.6, and vite ^8.0.0. Then replace next with vinext in your scripts and run as normal.
This project is under active development. Issues and PRs are welcome.
When you open a PR, CI (check, Vitest, Playwright E2E) runs automatically. First-time contributors need one manual approval from a maintainer, then subsequent PRs run without intervention.
Deploy previews (building and deploying examples to Cloudflare Workers) only run for branches pushed to the main repo. If you're a Cloudflare employee, push your branch to the main repo instead of forking, and previews deploy automatically. For fork PRs, a maintainer can comment /deploy-preview to trigger the deploy and post preview URLs.
If something doesn't work with your Next.js app, please file an issue — we want to hear about it.
Before you do, try pointing an AI agent at the problem. Open your project with Claude Code, Cursor, OpenCode, or whatever you use, and ask it to figure out why your app isn't working with vinext. In our experience, agents are very good at tracing through the vinext source, identifying the gap or bug, and often producing a fix or at least a clear diagnosis. An issue that includes "here's what the agent found" is significantly more actionable than "it doesn't work."
Even a partial diagnosis helps — stack traces, which next/* import is involved, whether it's a dev or production build issue, App Router vs Pages Router. The more context, the faster we can fix it.
MIT
| Back | FazBrowse Home | New Git URL |
{ "account_id": "<your-account-id>", ... }