vinext-vite-nextjs

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

vinext — Next.js API on Vite, Deploy Anywhere

vinext — 基于Vite的Next.js API,可部署至任意平台

Skill by ara.so — Daily 2026 Skills collection.
vinext is a Vite plugin that reimplements the Next.js public API surface (routing, SSR, RSC,
next/*
imports, CLI) so existing Next.js apps run on Vite instead of the Next.js compiler. It targets ~94% API coverage, supports both Pages Router and App Router, and deploys natively to Cloudflare Workers with optional Nitro support for AWS, Netlify, Vercel, and more.
ara.so提供的Skill — 2026每日技能合集。
vinext是一款Vite插件,重新实现了Next.js的公开API接口(路由、SSR、RSC、
next/*
导入、CLI),让现有Next.js应用可以基于Vite运行,而非Next.js编译器。它的API覆盖率约达94%,同时支持Pages Router和App Router,可原生部署至Cloudflare Workers,还可通过可选的Nitro支持部署至AWS、Netlify、Vercel等更多平台。

Installation

安装

New project (migrate from Next.js)

新项目(从Next.js迁移)

bash
undefined
bash
undefined

Automated one-command migration

一键自动化迁移

npx vinext init

This will:
1. Run compatibility check (`vinext check`)
2. Install `vite`, `@vitejs/plugin-react` as devDependencies
3. Install `@vitejs/plugin-rsc`, `react-server-dom-webpack` for App Router
4. Add `"type": "module"` to `package.json`
5. Rename CJS config files (e.g. `postcss.config.js` → `postcss.config.cjs`)
6. Add `dev:vinext` and `build:vinext` scripts
7. Generate a minimal `vite.config.ts`

Migration is **non-destructive** — Next.js still works alongside vinext.
npx vinext init

该命令会执行以下操作:
1. 运行兼容性检查(`vinext check`)
2. 安装`vite`、`@vitejs/plugin-react`作为开发依赖
3. 为App Router安装`@vitejs/plugin-rsc`、`react-server-dom-webpack`
4. 向`package.json`中添加`"type": "module"`
5. 重命名CJS配置文件(如`postcss.config.js` → `postcss.config.cjs`)
6. 添加`"dev:vinext"`和`"build:vinext"`脚本
7. 生成极简版`vite.config.ts`

迁移是**无破坏性的** —— Next.js仍可与vinext并行使用。

Manual installation

手动安装

bash
npm install -D vinext vite @vitejs/plugin-react
bash
npm install -D vinext vite @vitejs/plugin-react

App Router only:

仅App Router需要:

npm install -D @vitejs/plugin-rsc react-server-dom-webpack

Update `package.json` scripts:

```json
{
  "scripts": {
    "dev": "vinext dev",
    "build": "vinext build",
    "start": "vinext start",
    "deploy": "vinext deploy"
  }
}
npm install -D @vitejs/plugin-rsc react-server-dom-webpack

更新`package.json`中的脚本:

```json
{
  "scripts": {
    "dev": "vinext dev",
    "build": "vinext build",
    "start": "vinext start",
    "deploy": "vinext deploy"
  }
}

Agent Skill (AI-assisted migration)

Agent Skill(AI辅助迁移)

bash
npx skills add cloudflare/vinext
bash
npx skills add cloudflare/vinext

Then in your AI tool: "migrate this project to vinext"

然后在AI工具中输入:"将此项目迁移至vinext"

undefined
undefined

CLI Reference

CLI参考

CommandDescription
vinext dev
Start dev server with HMR
vinext build
Production build
vinext start
Local production server for testing
vinext deploy
Build + deploy to Cloudflare Workers
vinext init
Automated migration from Next.js
vinext check
Scan for compatibility issues before migrating
vinext lint
Delegate to eslint or oxlint
命令描述
vinext dev
启动带HMR的开发服务器
vinext build
生产环境构建
vinext start
启动本地生产服务器用于测试
vinext deploy
构建并部署至Cloudflare Workers
vinext init
从Next.js自动迁移
vinext check
迁移前扫描兼容性问题
vinext lint
委托给eslint或oxlint执行代码检查

CLI Options

CLI选项

bash
vinext dev -p 3001 -H 0.0.0.0
vinext deploy --preview
vinext deploy --env staging --name my-app
vinext deploy --skip-build --dry-run
vinext deploy --experimental-tpr
vinext init --port 3001 --skip-check --force
bash
vinext dev -p 3001 -H 0.0.0.0
vinext deploy --preview
vinext deploy --env staging --name my-app
vinext deploy --skip-build --dry-run
vinext deploy --experimental-tpr
vinext init --port 3001 --skip-check --force

Configuration

配置

vinext auto-detects
app/
or
pages/
directory and loads
next.config.js
automatically. No
vite.config.ts
is required for basic usage.
vinext会自动检测
app/
pages/
目录,并自动加载
next.config.js
。基础使用场景下无需
vite.config.ts

Minimal
vite.config.ts

极简版
vite.config.ts

typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'

export default defineConfig({
  plugins: [
    react(),
    vinext(),
  ],
})
typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'

export default defineConfig({
  plugins: [
    react(),
    vinext(),
  ],
})

App Router
vite.config.ts

App Router版
vite.config.ts

typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import rsc from '@vitejs/plugin-rsc'
import { vinext } from 'vinext/vite'

export default defineConfig({
  plugins: [
    react(),
    rsc(),
    vinext(),
  ],
})
typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import rsc from '@vitejs/plugin-rsc'
import { vinext } from 'vinext/vite'

export default defineConfig({
  plugins: [
    react(),
    rsc(),
    vinext(),
  ],
})

Cloudflare Workers with bindings

带绑定的Cloudflare Workers配置

typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'
import { cloudflare } from '@cloudflare/vite-plugin'

export default defineConfig({
  plugins: [
    cloudflare(),
    react(),
    vinext(),
  ],
})
typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'
import { cloudflare } from '@cloudflare/vite-plugin'

export default defineConfig({
  plugins: [
    cloudflare(),
    react(),
    vinext(),
  ],
})

Other platforms via Nitro

基于Nitro部署至其他平台

typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'
import nitro from 'vite-plugin-nitro'

export default defineConfig({
  plugins: [
    react(),
    vinext(),
    nitro({ preset: 'vercel' }), // or 'netlify', 'aws-amplify', 'deno-deploy', etc.
  ],
})
typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vinext } from 'vinext/vite'
import nitro from 'vite-plugin-nitro'

export default defineConfig({
  plugins: [
    react(),
    vinext(),
    nitro({ preset: 'vercel' }), // 或 'netlify', 'aws-amplify', 'deno-deploy'等
  ],
})

Project Structure

项目结构

vinext uses the same directory conventions as Next.js — no changes required:
my-app/
├── app/                  # App Router (auto-detected)
│   ├── layout.tsx
│   ├── page.tsx
│   └── api/route.ts
├── pages/                # Pages Router (auto-detected)
│   ├── index.tsx
│   └── api/hello.ts
├── public/               # Static assets
├── next.config.js        # Loaded automatically
├── package.json
└── vite.config.ts        # Optional for basic usage
vinext使用与Next.js相同的目录约定 —— 无需修改:
my-app/
├── app/                  # App Router(自动检测)
│   ├── layout.tsx
│   ├── page.tsx
│   └── api/route.ts
├── pages/                # Pages Router(自动检测)
│   ├── index.tsx
│   └── api/hello.ts
├── public/               # 静态资源
├── next.config.js        # 自动加载
├── package.json
└── vite.config.ts        # 基础使用场景下可选

Code Examples

代码示例

Pages Router — SSR page

Pages Router — SSR页面

typescript
// pages/index.tsx
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'

type Props = { data: string }

export const getServerSideProps: GetServerSideProps<Props> = async (ctx) => {
  return { props: { data: 'Hello from SSR' } }
}

export default function Home({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
  return <h1>{data}</h1>
}
typescript
// pages/index.tsx
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'

type Props = { data: string }

export const getServerSideProps: GetServerSideProps<Props> = async (ctx) => {
  return { props: { data: 'Hello from SSR' } }
}

export default function Home({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
  return <h1>{data}</h1>
}

Pages Router — Static generation

Pages Router — 静态生成

typescript
// pages/posts/[id].tsx
import type { GetStaticPaths, GetStaticProps } from 'next'

export const getStaticPaths: GetStaticPaths = async () => {
  return {
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    fallback: false,
  }
}

export const getStaticProps: GetStaticProps = async ({ params }) => {
  return { props: { id: params?.id } }
}

export default function Post({ id }: { id: string }) {
  return <p>Post {id}</p>
}
typescript
// pages/posts/[id].tsx
import type { GetStaticPaths, GetStaticProps } from 'next'

export const getStaticPaths: GetStaticPaths = async () => {
  return {
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    fallback: false,
  }
}

export const getStaticProps: GetStaticProps = async ({ params }) => {
  return { props: { id: params?.id } }
}

export default function Post({ id }: { id: string }) {
  return <p>Post {id}</p>
}

Pages Router — API route

Pages Router — API路由

typescript
// pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  res.status(200).json({ message: 'Hello from vinext' })
}
typescript
// pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  res.status(200).json({ message: 'Hello from vinext' })
}

App Router — Server Component

App Router — 服务器组件

typescript
// app/page.tsx
export default async function Page() {
  const data = await fetch('https://api.example.com/data').then(r => r.json())
  return <main>{data.title}</main>
}
typescript
// app/page.tsx
export default async function Page() {
  const data = await fetch('https://api.example.com/data').then(r => r.json())
  return <main>{data.title}</main>
}

App Router — Route Handler

App Router — 路由处理器

typescript
// app/api/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  return NextResponse.json({ status: 'ok' })
}

export async function POST(request: NextRequest) {
  const body = await request.json()
  return NextResponse.json({ received: body })
}
typescript
// app/api/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  return NextResponse.json({ status: 'ok' })
}

export async function POST(request: NextRequest) {
  const body = await request.json()
  return NextResponse.json({ received: body })
}

App Router — Server Action

App Router — 服务器操作

typescript
// app/actions.ts
'use server'

export async function submitForm(formData: FormData) {
  const name = formData.get('name')
  // server-side logic here
  return { success: true, name }
}
typescript
// app/form.tsx
'use client'
import { submitForm } from './actions'

export function Form() {
  return (
    <form action={submitForm}>
      <input name="name" />
      <button type="submit">Submit</button>
    </form>
  )
}
typescript
// app/actions.ts
'use server'

export async function submitForm(formData: FormData) {
  const name = formData.get('name')
  // 此处为服务器端逻辑
  return { success: true, name }
}
typescript
// app/form.tsx
'use client'
import { submitForm } from './actions'

export function Form() {
  return (
    <form action={submitForm}>
      <input name="name" />
      <button type="submit">Submit</button>
    </form>
  )
}

Middleware

中间件

typescript
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const token = request.cookies.get('token')
  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*'],
}
typescript
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const token = request.cookies.get('token')
  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*'],
}

Cloudflare Workers — Bindings access

Cloudflare Workers — 绑定访问

typescript
// app/api/kv/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { getCloudflareContext } from 'cloudflare:workers'

export async function GET(request: NextRequest) {
  const { env } = getCloudflareContext()
  const value = await env.MY_KV.get('key')
  return NextResponse.json({ value })
}
typescript
// app/api/kv/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { getCloudflareContext } from 'cloudflare:workers'

export async function GET(request: NextRequest) {
  const { env } = getCloudflareContext()
  const value = await env.MY_KV.get('key')
  return NextResponse.json({ value })
}

Image optimization

图片优化

typescript
// app/page.tsx
import Image from 'next/image'

export default function Page() {
  return (
    <Image
      src="/hero.png"
      alt="Hero"
      width={800}
      height={400}
      priority
    />
  )
}
typescript
// app/page.tsx
import Image from 'next/image'

export default function Page() {
  return (
    <Image
      src="/hero.png"
      alt="Hero"
      width={800}
      height={400}
      priority
    />
  )
}

Link and navigation

链接与导航

typescript
// app/nav.tsx
'use client'
import Link from 'next/link'
import { useRouter, usePathname } from 'next/navigation'

export function Nav() {
  const router = useRouter()
  const pathname = usePathname()

  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>
      <button onClick={() => router.push('/dashboard')}>Dashboard</button>
    </nav>
  )
}
typescript
// app/nav.tsx
'use client'
import Link from 'next/link'
import { useRouter, usePathname } from 'next/navigation'

export function Nav() {
  const router = useRouter()
  const pathname = usePathname()

  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>
      <button onClick={() => router.push('/dashboard')}>Dashboard</button>
    </nav>
  )
}

Deployment

部署

Cloudflare Workers

Cloudflare Workers

bash
undefined
bash
undefined

Authenticate (once)

认证(仅需一次)

wrangler login
wrangler login

Deploy

部署

vinext deploy
vinext deploy

Deploy to preview

部署至预览环境

vinext deploy --preview
vinext deploy --preview

Deploy to named environment

部署至指定命名环境

vinext deploy --env production --name my-production-app

For CI/CD, set `CLOUDFLARE_API_TOKEN` environment variable instead of `wrangler login`.
vinext deploy --env production --name my-production-app

对于CI/CD,设置`CLOUDFLARE_API_TOKEN`环境变量即可,无需执行`wrangler login`。

wrangler.toml
(Cloudflare config)

wrangler.toml
(Cloudflare配置)

toml
name = "my-app"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]

[[kv_namespaces]]
binding = "MY_KV"
id = "your-kv-namespace-id"

[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"
toml
name = "my-app"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]

[[kv_namespaces]]
binding = "MY_KV"
id = "your-kv-namespace-id"

[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"

Netlify / Vercel / AWS via Nitro

基于Nitro部署至Netlify / Vercel / AWS

bash
npm install -D vite-plugin-nitro
bash
npm install -D vite-plugin-nitro

Then add nitro plugin to vite.config.ts with your target preset

然后在vite.config.ts中添加nitro插件并指定目标预设

nitro({ preset: 'netlify' })

nitro({ preset: 'netlify' })

nitro({ preset: 'vercel' })

nitro({ preset: 'vercel' })

nitro({ preset: 'aws-amplify' })

nitro({ preset: 'aws-amplify' })

undefined
undefined

next.config.js
Support

next.config.js
支持

vinext loads your existing
next.config.js
automatically:
javascript
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: 'images.example.com' },
    ],
  },
  env: {
    MY_VAR: process.env.MY_VAR,
  },
  redirects: async () => [
    { source: '/old', destination: '/new', permanent: true },
  ],
  rewrites: async () => [
    { source: '/api/:path*', destination: 'https://backend.example.com/:path*' },
  ],
}

module.exports = nextConfig
vinext会自动加载现有
next.config.js
javascript
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: 'images.example.com' },
    ],
  },
  env: {
    MY_VAR: process.env.MY_VAR,
  },
  redirects: async () => [
    { source: '/old', destination: '/new', permanent: true },
  ],
  rewrites: async () => [
    { source: '/api/:path*', destination: 'https://backend.example.com/:path*' },
  ],
}

module.exports = nextConfig

Compatibility Check

兼容性检查

Run before migrating to identify unsupported features:
bash
npx vinext check
This scans for:
  • Unsupported
    next.config.js
    options
  • Deprecated Pages Router APIs
  • Experimental Next.js features not yet supported
  • CJS config file conflicts
迁移前运行以下命令,识别不支持的功能:
bash
npx vinext check
该命令会扫描:
  • 不支持的
    next.config.js
    选项
  • 已废弃的Pages Router API
  • 尚未支持的实验性Next.js功能
  • CJS配置文件冲突

Common Patterns

常见模式

Environment variables

环境变量

Works the same as Next.js —
.env
,
.env.local
,
.env.production
:
bash
undefined
与Next.js工作方式相同 —— 支持
.env
,
.env.local
,
.env.production
bash
undefined

.env.local

.env.local

NEXT_PUBLIC_API_URL=https://api.example.com DATABASE_URL=$DATABASE_URL

```typescript
// Accessible in client code (NEXT_PUBLIC_ prefix)
const apiUrl = process.env.NEXT_PUBLIC_API_URL

// Server-only
const dbUrl = process.env.DATABASE_URL
NEXT_PUBLIC_API_URL=https://api.example.com DATABASE_URL=$DATABASE_URL

```typescript
// 客户端代码可访问(带NEXT_PUBLIC_前缀)
const apiUrl = process.env.NEXT_PUBLIC_API_URL

// 仅服务器端可访问
const dbUrl = process.env.DATABASE_URL

TypeScript path aliases

TypeScript路径别名

json
// tsconfig.json — works as-is
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
json
// tsconfig.json —— 可直接使用
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Tailwind CSS

Tailwind CSS

bash
npm install -D tailwindcss postcss autoprefixer
bash
npm install -D tailwindcss postcss autoprefixer

Rename postcss.config.js → postcss.config.cjs (vinext init does this automatically)

重命名postcss.config.js → postcss.config.cjs(vinext init会自动完成此操作)


```javascript
// postcss.config.cjs
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

```javascript
// postcss.config.cjs
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

Troubleshooting

故障排除

ESM conflicts with CJS config files

ESM与CJS配置文件冲突

bash
undefined
bash
undefined

vinext init handles this automatically, or rename manually:

vinext init会自动处理,或手动重命名:

mv postcss.config.js postcss.config.cjs mv tailwind.config.js tailwind.config.cjs

Ensure `package.json` has `"type": "module"`.
mv postcss.config.js postcss.config.cjs mv tailwind.config.js tailwind.config.cjs

确保`package.json`中包含`"type": "module"`。

vinext init
overwrites existing
vite.config.ts

vinext init
覆盖现有
vite.config.ts

bash
vinext init --force
bash
vinext init --force

Skip compatibility check during init

迁移初始化时跳过兼容性检查

bash
vinext init --skip-check
bash
vinext init --skip-check

Custom port

自定义端口

bash
vinext dev -p 3001
vinext init --port 3001
bash
vinext dev -p 3001
vinext init --port 3001

wrangler
not authenticated for deploy

部署时
wrangler
未认证

bash
wrangler login
bash
wrangler login

or set env var:

或设置环境变量:

export CLOUDFLARE_API_TOKEN=your_token_here
undefined
export CLOUDFLARE_API_TOKEN=your_token_here
undefined

Dry-run deploy to verify config

预演部署以验证配置

bash
vinext deploy --dry-run
bash
vinext deploy --dry-run

App Router multi-environment build issues

App Router多环境构建问题

App Router builds produce three environments (RSC + SSR + client). If you see build errors, ensure all three plugins are installed:
bash
npm install -D @vitejs/plugin-rsc react-server-dom-webpack
And your
vite.config.ts
includes both
react()
and
rsc()
plugins in the correct order.
App Router构建会生成三个环境(RSC + SSR + 客户端)。如果遇到构建错误,请确保已安装所有三个插件:
bash
npm install -D @vitejs/plugin-rsc react-server-dom-webpack
并且
vite.config.ts
中按正确顺序包含
react()
rsc()
插件。

What's Supported (~94% of Next.js API)

支持的功能(约Next.js API的94%)

  • ✅ Pages Router (SSR, SSG, ISR, API routes)
  • ✅ App Router (RSC, Server Actions, Route Handlers, Layouts, Loading, Error boundaries)
  • ✅ Middleware
  • next/image
    ,
    next/link
    ,
    next/router
    ,
    next/navigation
    ,
    next/head
  • next/font
    ,
    next/dynamic
  • next.config.js
    (redirects, rewrites, headers, env, images)
  • ✅ Cloudflare Workers native deployment with bindings
  • ✅ HMR in development
  • ✅ TypeScript, Tailwind CSS, CSS Modules
  • ⚠️ Experimental Next.js features — lower priority
  • ❌ Undocumented Vercel-specific behavior — intentionally not supported
  • ✅ Pages Router(SSR、SSG、ISR、API路由)
  • ✅ App Router(RSC、服务器操作、路由处理器、布局、加载、错误边界)
  • ✅ 中间件
  • next/image
    ,
    next/link
    ,
    next/router
    ,
    next/navigation
    ,
    next/head
  • next/font
    ,
    next/dynamic
  • next.config.js
    (重定向、重写、响应头、环境变量、图片配置)
  • ✅ 带绑定的Cloudflare Workers原生部署
  • ✅ 开发环境下的HMR
  • ✅ TypeScript、Tailwind CSS、CSS Modules
  • ⚠️ 实验性Next.js功能 —— 优先级较低
  • ❌ 未公开的Vercel特定行为 —— 有意不支持