npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

openvite

v0.0.2

Published

Run Next.js apps on Vite. Drop-in replacement for the next CLI.

Readme

openvite

npm version CI License: MIT

The Next.js API surface, reimplemented on Vite.

Experimental — under heavy development. There will be bugs, rough edges, and things that don't work. Use at your own risk.

Quick start

openvite includes an Agent Skill that handles migration for you. It works with Claude Code, OpenCode, Cursor, Codex, and dozens of other AI coding tools. Install it, open your Next.js project, and tell the AI to migrate:

npx skills add openvite/openvite

Then open your Next.js project in any supported tool and say:

migrate this project to openvite

The skill handles compatibility checking, dependency installation, config generation, and dev server startup. It knows what openvite supports and will flag anything that needs manual attention.

Or do it manually

npm install openvite

Replace next with openvite in your scripts:

{
  "scripts": {
    "dev": "openvite dev",
    "build": "openvite build",
    "start": "openvite start"
  }
}
openvite dev          # Development server with HMR
openvite build        # Production build
openvite deploy       # Build and deploy to Cloudflare Workers

openvite 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 openvite check first to scan for known compatibility issues, or use openvite init to automate the full migration.

CLI reference

| Command | Description | |---------|-------------| | openvite dev | Start dev server with HMR | | openvite build | Production build (multi-environment for App Router: RSC + SSR + client) | | openvite start | Start local production server for testing | | openvite deploy | Build and deploy to Cloudflare Workers | | openvite init | Migrate a Next.js project to run under openvite | | openvite check | Scan your Next.js app for compatibility issues before migrating | | openvite lint | Delegate to eslint or oxlint |

Options: -p / --port <port>, -H / --hostname <host>, --turbopack (accepted, no-op).

openvite deploy options: --preview, --env <name>, --name <name>, --skip-build, --dry-run, --experimental-tpr.

openvite init options: --port <port> (default: 3001), --skip-check, --force.

Starting a new openvite project

Run npm create next-app@latest to create a new Next.js project, then follow the instructions above to migrate it to openvite.

Migrating an existing Next.js project

openvite init automates the migration in one command:

npx openvite init

This will:

  1. Run openvite check to scan for compatibility issues
  2. Install vite (and @vitejs/plugin-rsc for App Router projects) as devDependencies
  3. Rename CJS config files (e.g. postcss.config.js -> .cjs) to avoid ESM conflicts
  4. Add "type": "module" to package.json
  5. Add dev:openvite and build:openvite scripts to package.json
  6. Generate a minimal vite.config.ts

The migration is non-destructive -- your existing Next.js setup continues to work alongside openvite. It does not modify next.config, tsconfig.json, or any source files, and it does not remove Next.js dependencies.

npm run dev:openvite    # Start the openvite dev server (port 3001)
npm run dev           # Still runs Next.js as before

Use --force to overwrite an existing vite.config.ts, or --skip-check to skip the compatibility report.

Why

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.

openvite is an experiment: can we reimplement the Next.js API surface on Vite, so that existing Next.js applications can run on a completely different toolchain? The answer, so far, is mostly yes — about 94% of the API surface works.

openvite works everywhere. It natively supports Cloudflare Workers (with openvite deploy, bindings, KV caching), and can be deployed to Vercel, Netlify, AWS, Deno Deploy, and more via the Nitro Vite plugin.

Alternatives worth knowing about:

  • OpenNext — adapts next build output for AWS, Cloudflare, and other platforms. OpenNext has been around much longer than openvite, is more mature, and covers more of the Next.js API surface because it builds on top of Next.js's own output rather than reimplementing it. If you want the safer, more proven option, start there.
  • Next.js self-hosting — Next.js can be deployed to any Node.js server, Docker container, or as a static export.

Design principles

  • Deploy anywhere. Natively supports Cloudflare Workers, with other platforms available via Nitro.
  • Pragmatic compatibility, not bug-for-bug parity. Targets 95%+ of real-world Next.js apps. Edge cases that depend on undocumented Vercel behavior are intentionally not supported.
  • Latest Next.js only. Targets Next.js 16.x. No support for deprecated APIs from older versions.
  • Incremental adoption. Drop in the plugin, fix what breaks, deploy.

FAQ

What is this? openvite 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.

Is this a fork of Next.js? No. openvite is an alternative implementation of the Next.js API surface built on Vite. It does import some Next.js types and utilities, but the core is written from scratch. The goal is not to create a competing framework or add features beyond what Next.js offers — it's an experiment in how far AI-driven development and Vite's toolchain can go in replicating an existing, well-defined API surface.

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. openvite 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're interested in experimenting with a lighter toolchain and don't need every Next.js API, openvite might be worth a look.

Can I use this in production? You can, with caution. This is experimental software with known bugs. It works well enough for demos and exploration, but it hasn't been battle-tested with real production traffic.

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 2,100 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 openvite as an integration test. See the Tests section and tests/nextjs-compat/TRACKING.md for details.

Who is reviewing this code? Mostly nobody. This is an experiment in seeing how far AI-driven development can go. The test suite is the primary quality gate — not human code review. Contributions and code review are 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. This project is an experiment to see how much of the Next.js developer experience can be replicated on top of Vite's 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 openvite, 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 (openvite deploy) gives you the smoothest experience.

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.

Deployment

Cloudflare Workers

openvite has native integration with Cloudflare Workers through @cloudflare/vite-plugin, including bindings access via cloudflare:workers, KV caching, image optimization, and the openvite deploy one-command workflow.

openvite deploy auto-generates the necessary configuration files (vite.config.ts, wrangler.jsonc, worker/index.ts) if they don't exist, builds the application, and deploys to Workers.

openvite deploy
openvite deploy --env staging

Use --env <name> to target wrangler.jsonc env.<name>. --preview is shorthand for --env preview.

The deploy command also auto-detects and fixes common migration issues:

  • Adds "type": "module" to package.json if missing
  • Resolves tsconfig.json path aliases automatically (via vite-tsconfig-paths)
  • Detects MDX usage and configures @mdx-js/rollup
  • Renames CJS config files (postcss.config.js, etc.) to .cjs when needed
  • Detects native Node.js modules (sharp, resvg, satori, lightningcss, @napi-rs/canvas) and auto-stubs them for Workers. If you encounter others that need stubbing, PRs are welcome.

Both App Router and Pages Router work on Workers with full client-side hydration.

Cloudflare Bindings (D1, R2, KV, AI, etc.)

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 openvite.

Traffic-aware Pre-Rendering (experimental)

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.

openvite deploy --experimental-tpr                    # Pre-render pages covering 90% of traffic
openvite deploy --experimental-tpr --tpr-coverage 95  # More aggressive coverage
openvite deploy --experimental-tpr --tpr-limit 500    # Cap at 500 pages
openvite deploy --experimental-tpr --tpr-window 48    # Use 48h of analytics

Requires a custom domain (zone analytics are unavailable on *.workers.dev) and CLOUDFLARE_API_TOKEN with Zone.Analytics read permission.

For production caching (ISR), use the built-in Cloudflare KV cache handler:

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

setCacheHandler(new KVCacheHandler(env.MY_KV_NAMESPACE));

Custom Vite configuration

If you need to customize the Vite config, create a vite.config.ts. openvite will merge its config with yours. This is required for Cloudflare Workers deployment with the App Router (RSC needs explicit plugin configuration):

import { defineConfig } from "vite";
import openvite from "openvite";
import rsc from "@vitejs/plugin-rsc";
import { cloudflare } from "@cloudflare/vite-plugin";

export default defineConfig({
  plugins: [
    openvite(),
    rsc({
      entries: {
        rsc: "virtual:openvite-rsc-entry",
        ssr: "virtual:openvite-app-ssr-entry",
        client: "virtual:openvite-app-browser-entry",
      },
    }),
    cloudflare({
      viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] },
    }),
  ],
});

See the examples for complete working configurations.

Other platforms (via Nitro)

For deploying to platforms other than Cloudflare, openvite works with Nitro as a Vite plugin. Add nitro alongside openvite in your Vite config and deploy to any Nitro-supported platform.

import { defineConfig } from "vite";
import openvite from "openvite";
import { nitro } from "nitro/vite";

export default defineConfig({
  plugins: [openvite(), nitro()],
});
npm install nitro

Nitro 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 build

Deploying to Cloudflare? You can use Nitro, but the native integration (openvite deploy / @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 build

Deploy 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.

Nitro auto-detects Netlify in CI. For local builds:

NITRO_PRESET=netlify npx vite build

Deploy with the Netlify CLI or connect your Git repo. Set the build command to vite build.

Nitro auto-detects AWS Amplify in CI. For local builds:

NITRO_PRESET=aws_amplify npx vite build

Connect your Git repo in the AWS Amplify console. Set the build command to vite build.

NITRO_PRESET=deno_deploy npx vite build
cd .output
deployctl deploy --project=my-project server/index.ts
NITRO_PRESET=node npx vite build
node .output/server/index.mjs

This 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.

Examples

| Example | Description | Source | |---------|-------------|--------| | App Router Playground | Vercel's Next.js App Router Playground running on openvite | examples/app-router-playground | | Hacker News | HN clone (App Router, RSC) | examples/hackernews | | Nextra Docs | Nextra docs site (MDX, App Router) | examples/nextra-docs-template | | App Router (Cloudflare) | Minimal App Router on Workers | examples/app-router-cloudflare | | Pages Router (Cloudflare) | Minimal Pages Router on Workers | examples/pages-router-cloudflare | | App Router + Nitro | App Router deployed via Nitro (multi-platform) | examples/app-router-nitro | | RealWorld API | REST API routes example | examples/realworld-api-rest | | Benchmarks Dashboard | Build performance tracking (D1-backed) | examples/benchmarks |

API coverage

~94% of the Next.js 16 API surface has full or partial support. The remaining gaps are intentional stubs for deprecated features and Partial Prerendering (which Next.js 16 reworked into "use cache" — that directive is fully supported).

Full = complete implementation | Partial = runtime behavior correct, some build-time optimizations missing | Stub = intentional no-op

Module shims

Every next/* import is shimmed to a Vite-compatible implementation.

| Module | Status | Notes | |--------|--------|-------| | next/link | Full | All props including prefetch (IntersectionObserver), onNavigate, scroll restoration, basePath, locale | | next/image | Partial | Remote images via @unpic/react (28 CDNs). Local images via <img> + srcSet. No build-time optimization/resizing | | next/head | Full | SSR collection + client-side DOM manipulation | | next/router | Full | useRouter, Router singleton, events, client-side navigation, SSR context, i18n | | next/navigation | Full | usePathname, useSearchParams, useParams, useRouter, redirect, notFound, forbidden, unauthorized | | next/server | Full | NextRequest, NextResponse, NextURL, cookies, userAgent, after, connection, URLPattern | | next/headers | Full | Async headers(), cookies(), draftMode() | | next/dynamic | Full | ssr: true, ssr: false, loading component | | next/script | Full | All 4 strategies (beforeInteractive, afterInteractive, lazyOnload, worker) | | next/font/google | Partial | Runtime CDN loading. No self-hosting, font subsetting, or fallback metrics | | next/font/local | Partial | Runtime @font-face injection. Not extracted at build time | | next/og | Full | OG image generation via @vercel/og (Satori + resvg) | | next/cache | Full | revalidateTag, revalidatePath, unstable_cache, pluggable CacheHandler, "use cache" with cacheLife() and cacheTag() | | next/form | Full | GET form interception + POST server action delegation | | next/legacy/image | Full | Translates legacy props to modern Image | | next/error | Full | Default error page component | | next/config | Full | getConfig / setConfig | | next/document | Full | Html, Head, Main, NextScript | | next/constants | Full | All phase constants | | next/amp | Stub | No-op (AMP is deprecated) | | next/web-vitals | Stub | No-op (use the web-vitals library directly) |

Routing

| Feature | Status | Notes | |---------|--------|-------| | File-system routing (pages/) | Full | Automatic scanning with hot-reload on file changes | | File-system routing (app/) | Full | Pages, routes, layouts, templates, loading, error, not-found, forbidden, unauthorized | | Dynamic routes [param] | Full | Both routers | | Catch-all [...slug] | Full | Both routers | | Optional catch-all [[...slug]] | Full | Both routers | | Route groups (group) | Full | URL-transparent, layouts still apply | | Parallel routes @slot | Full | Discovery, layout props, default.tsx, inherited slots | | Intercepting routes | Full | (.), (..), (..)(..), (...) conventions | | Route handlers (route.ts) | Full | Named HTTP methods, auto OPTIONS/HEAD, cookie attachment | | Middleware | Full | middleware.ts and proxy.ts (Next.js 16). Matcher patterns (string, array, regex, :param, :path*, :path+) | | i18n routing | Partial | Pages Router locale prefix, Accept-Language detection, NEXT_LOCALE cookie. No domain-based routing | | basePath | Full | Applied everywhere — URLs, Link, Router, navigation hooks | | trailingSlash | Full | 308 redirects to canonical form |

Server features

| Feature | Status | Notes | |---------|--------|-------| | SSR (Pages Router) | Full | Streaming, _app/_document, __NEXT_DATA__, hydration | | SSR (App Router) | Full | RSC pipeline, nested layouts, streaming, nav context for client components | | getStaticProps | Full | Props, redirect, notFound, revalidate | | getStaticPaths | Full | fallback: false, true, "blocking" | | getServerSideProps | Full | Full context including locale | | ISR | Full | Stale-while-revalidate, pluggable CacheHandler, background regeneration | | Server Actions ("use server") | Full | Action execution, FormData, re-render after mutation, redirect() in actions | | React Server Components | Full | Via @vitejs/plugin-rsc. "use client" boundaries work correctly | | Streaming SSR | Full | Both routers | | Metadata API | Full | metadata, generateMetadata, viewport, generateViewport, title templates | | generateStaticParams | Full | With dynamicParams enforcement | | Metadata file routes | Full | sitemap.xml, robots.txt, manifest, favicon, OG images (static + dynamic) | | Static export (output: 'export') | Full | Generates static HTML/JSON for all routes | | connection() | Full | Forces dynamic rendering | | "use cache" directive | Full | File-level and function-level. cacheLife() profiles, cacheTag(), stale-while-revalidate | | instrumentation.ts | Full | register() and onRequestError() callbacks | | Route segment config | Partial | revalidate, dynamic, dynamicParams. runtime and preferredRegion are ignored |

Configuration

| Feature | Status | Notes | |---------|--------|-------| | next.config.js / .ts / .mjs | Full | Function configs, phase argument | | rewrites / redirects / headers | Full | All phases, param interpolation | | Environment variables (.env*, NEXT_PUBLIC_*) | Full | Auto-loads Next.js-style dotenv files; only public vars are inlined | | images config | Partial | Parsed but not used for optimization |

Environment variable loading (.env*)

openvite automatically loads dotenv files for dev, build, start, and deploy.

Load order matches Next.js (highest priority first):

  1. Existing process.env values (shell/CI)
  2. .env.<mode>.local
  3. .env.local (skipped when mode is test)
  4. .env.<mode>
  5. .env

Modes:

  • openvite dev uses development
  • openvite build, openvite start, and openvite deploy use production

Variable expansion ($VAR / ${VAR}) is supported.

Client exposure remains explicit:

  • NEXT_PUBLIC_* variables are inlined for browser usage
  • next.config.js env entries are also inlined
  • Other env vars stay server-only unless you explicitly expose them through Vite (for example VITE_* + import.meta.env)

Override behavior:

  • To override any .env* value, set it in your shell/CI environment before running openvite. Existing process.env always wins.

Caching

The cache is pluggable. The default MemoryCacheHandler works out of the box. Swap in your own backend for production:

import { setCacheHandler } from "next/cache";
setCacheHandler(new MyCacheHandler()); // Redis, DynamoDB, etc.

The CacheHandler interface matches Next.js 16's shape, so community adapters should be compatible.

What's NOT supported (and won't be)

These are intentional exclusions:

  • Vercel-specific features@vercel/og edge runtime, Vercel Analytics integration, Vercel KV/Blob/Postgres bindings. Use platform equivalents.
  • AMP — Deprecated since Next.js 13. useAmp() returns false.
  • next export (legacy) — Use output: 'export' in config instead.
  • Turbopack/webpack configuration — This runs on Vite. Use Vite plugins instead of webpack loaders/plugins.
  • next/jest — Use Vitest.
  • create-next-app scaffolding — Not a goal.
  • Bug-for-bug parity with undocumented behavior — If it's not in the Next.js docs, we probably don't replicate it.

Known limitations

  • Image optimization doesn't happen at build time. Remote images work via @unpic/react (auto-detects 28 CDN providers). Local images are routed through a /_openvite/image endpoint that can resize and transcode on Cloudflare Workers (via the Images binding) in production, but no build-time optimization or static resizing occurs.
  • Google Fonts are loaded from the CDN, not self-hosted. No size-adjust fallback font metrics. Local fonts work but @font-face CSS is injected at runtime, not extracted at build time.
  • useSelectedLayoutSegment(s) derives segments from the pathname rather than being truly layout-aware. May differ from Next.js in edge cases with parallel routes.
  • Route segment configruntime and preferredRegion are ignored (everything runs in the same environment).
  • Node.js production server (openvite start) works for testing but is less complete than Workers deployment. Cloudflare Workers is the primary target.
  • Native Node modules (sharp, resvg, satori, lightningcss, @napi-rs/canvas) crash Vite's RSC dev environment. Dynamic OG image/icon routes using these work in production builds but not in dev mode. These are auto-stubbed during openvite deploy.

Benchmarks

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 openvite 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 openvite 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 16 (Turbopack) against openvite on both Vite 7 (Rollup) and Vite 8 (Rolldown). Turbopack and Rolldown both parallelize across cores, so results on machines with more cores may differ significantly.

We measure three things:

  • Production build time — 5 runs, timed with hyperfine.
  • Client bundle size — gzipped output of each build.
  • Dev server cold start — 10 runs, randomized execution order. Vite's dependency optimizer cache is cleared before each run.

Benchmarks run on GitHub CI runners (2-core Ubuntu) on every merge to main.

Analysis of the build output shows two main factors:

  1. Tree-shaking: Vite/Rollup produces a smaller React+ReactDOM bundle than Next.js/Turbopack. Rollup's more aggressive dead-code elimination accounts for roughly half the overall difference.
  2. Framework overhead: Next.js ships more client-side infrastructure (router, Turbopack runtime loader, prefetching, error handling) than openvite's lighter client runtime.

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.

Architecture

openvite follows a hexagonal (ports & adapters) architecture:

packages/openvite/src/
  core/                     # Domain logic
    plugin.ts               # Orchestrator — composes all plugins
    plugin-context.ts       # Shared state (routes, config, platform adapter)
    types.ts                # Public types, constants, virtual module IDs
    platform.ts             # PlatformAdapter interface (port)
  plugins/                  # Individual Vite plugins (1 file = 1 plugin)
    config.ts               # openvite:config — resolve structure, load next.config, aliases
    pages-router.ts         # openvite:pages-router — HMR, file watching, dev middleware
    google-fonts.ts         # openvite:google-fonts — transform next/font/google imports
    local-fonts.ts          # openvite:local-fonts — transform next/font/local imports
    image-imports.ts        # openvite:image-imports — rewrite next/image static imports
    image-config.ts         # openvite:image-config — inject image loader config
    og-assets.ts            # openvite:og-assets — resolve OG image assets
    react-canary.ts         # openvite:react-canary — polyfill React canary features
    use-cache.ts            # openvite:use-cache — transform "use cache" directives
    platform-build.ts       # openvite:platform-build — post-build platform delegation
  codegen/                  # Virtual module generators
    pages-entry.ts          # Pages Router server + client entries
    rsc-entry.ts            # App Router RSC entry
    ssr-entry.ts            # App Router SSR entry
    browser-entry.ts        # App Router browser entry
  platform/                 # Platform adapters (implementations)
    detect.ts               # Auto-detect platform from installed plugins
    node.ts                 # Node.js adapter
    cloudflare.ts           # Cloudflare Workers adapter
    nitro.ts                # Nitro adapter
  routing/                  # File-system routers
    app-router.ts           # App Router scanner (app/ conventions)
    pages-router.ts         # Pages Router scanner (pages/ conventions)
  server/                   # SSR request handlers
    dev-server.ts           # Pages Router SSR handler (dev)
    app-dev-server.ts       # App Router SSR handler (dev)
    prod-server.ts          # Production HTTP server with compression
    api-handler.ts          # Pages Router API routes
    middleware.ts            # middleware.ts / proxy.ts runner
    isr-cache.ts            # ISR cache (stale-while-revalidate)
    image-optimization.ts   # /_openvite/image endpoint
    metadata-routes.ts      # File-based metadata route scanner
    instrumentation.ts      # instrumentation.ts support
  cloudflare/               # Cloudflare-specific
    kv-cache-handler.ts     # KV-backed CacheHandler for ISR
    tpr.ts                  # Traffic-aware Pre-Rendering
  shims/                    # next/* module reimplementations (33 shims + 6 internal)
  build/
    static-export.ts        # output: 'export' support
  config/
    next-config.ts          # next.config.js loader
    config-matchers.ts      # Rewrite/redirect matching utilities
    dotenv.ts               # .env file loading
  client/
    entry.ts                # Client-side hydration entry
  utils/
    project.ts              # Package manager detection, ESM/CJS helpers
    hash.ts                 # Hashing utilities
    query.ts                # Query string 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

examples/                   # Deployed demo apps (see Examples above)

Pages Router flow

Request -> Vite dev server middleware -> Route match -> getServerSideProps/getStaticProps
  -> renderToReadableStream(App + Page) -> HTML with __NEXT_DATA__ -> Client hydration

App Router flow

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

Tests

bun run test             # 2,100+ Vitest unit + integration tests
bun run test:e2e         # 380+ Playwright E2E tests
bun run typecheck        # TypeScript checking (tsgo)
bun run lint             # Linting (oxlint)

E2E tests cover Pages Router (dev + production) and App Router (dev).

The Vercel App Router Playground runs on openvite as an integration test — see examples/app-router-playground.

Local setup (from source)

If you're working from the repo instead of installing from npm:

git clone https://github.com/openvite/openvite.git
cd openvite
bun install
bun run build    # esbuild (JS) + tsgo (declarations) in parallel, ~880ms

This compiles the openvite package to packages/openvite/dist/. For active development, use cd packages/openvite && bun run dev to run tsc --watch.

To use it against an external Next.js app, link the built package:

# From your Next.js project directory:
bun link /path/to/openvite/packages/openvite

Or add it to your package.json as a file dependency:

{
  "dependencies": {
    "openvite": "file:/path/to/openvite/packages/openvite"
  }
}

openvite has peer dependencies on react >=19.2.0, react-dom >=19.2.0, and vite ^7.0.0. Then replace next with openvite in your scripts and run as normal.

Contributing

This project is experimental and under active development. Issues and PRs are welcome.

CI

When you open a PR, CI (lint, typecheck, Vitest, Playwright E2E) runs automatically. First-time contributors need one manual approval from a maintainer, then subsequent PRs run without intervention.

Reporting bugs

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 openvite. In our experience, agents are very good at tracing through the openvite 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.

License

MIT