百科.dev
全部条目AI 编程趋势榜开源项目技术资讯提交条目
登录
< 返回工具列表
V

vidact

> 编程语言
开源

一个编译器,它将 React 兼容的代码转换为不使用虚拟 DOM 的 VanillaJS

783 stars0 点赞0 次浏览
访问官网GitHub

工具介绍

一个编译器,它将 React 兼容的代码转换为不使用虚拟 DOM 的 VanillaJS

Vidact is a compiler for React-shaped components. You write function components, JSX, and hooks exactly the way you would in a React application. Instead of shipping React, Vidact compiles each component into plain DOM operations: create these elements once, and when this piece of state changes, update this text node.

There is no Virtual DOM, no reconciler, and no React runtime in your bundle.

[!WARNING] Vidact is in beta. APIs are stabilizing and the supported subset of React is growing. Pin @vidact/runtime, @vidact/vite, and @vidact/start to matching versions and run your test suite against the versions you ship.

Overview

Components run once, when they mount. The compiler reads each function ahead of time, works out which parts of the output depend on which values, and emits a fixed list of small updaters. A state write runs only the updaters that read that state. The component function is never called again.

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)

  return (
    

  )
}

Compiled by Vidact, this builds a , a , and an `` once, then keeps one updater that rewrites the text node whenever count changes.

Why Vidact?

  • Small bundles. A compiled counter is about 8 kB gzipped with the runtime included, because there is no reconciler to download.
  • Predictable updates. A state write runs a known list of updaters, so there are no surprise re-renders, no stale closures, and no memo to keep things fast.
  • Familiar source shape. Vidact supports the function-component, JSX, hook, DOM, SSR, and feature-gated APIs listed in the React compatibility matrix. Compatibility is syntax- and target-specific; unsupported forms fail the build.
  • Loud failures. Code Vidact cannot compile fails the build at the exact source location instead of falling back to a slower path.
  • Full stack when you want it. Vidact Start adds file-based routing, loaders, server rendering, hydration, and client navigation.

What Vidact is not. Vidact is not a React renderer and does not run React. There is no element tree, no Fiber, and no React DevTools. Class components, React.Children, and libraries that reach into React internals are not supported. See the React compatibility matrix for the full contract.

Quick start

The fastest way to start is the project generator:

npx vidact my-app

It asks for a template and leaves you with a project you can run:

Template What you get
spa Vite, the Vidact compiler plugin, and a client-rendered entry point
start Vidact Start with file routes, loaders, server rendering, and hydration
nitro The same full-stack app served by Nitro, with a preset for every major host

Manual setup

Install the runtime, the Vite plugin, and the React-shaped types. react and react-dom are missing from this list on purpose: your source imports from react, and the Vite plugin resolves those imports to the compiled runtime.

pnpm add @vidact/runtime
pnpm add -D @vidact/vite @vidact/react-types @types/react typescript vite

Add the plugin to Vite. It compiles every .tsx file in the project.

// vite.config.ts
import { vidact } from '@vidact/vite'
import { defineConfig } from 'vite'

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

Tell TypeScript to leave JSX alone and to type it with Vidact's React-shaped declarations:

{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "@vidact/react-types",
    "types": ["@vidact/react-types", "vite/client"]
  }
}

Mount a component. mountCompiled takes the component itself rather than a JSX element, because a compiled component is a factory that builds its own DOM.

// src/main.ts
import { mountCompiled } from '@vidact/runtime'

import { Counter } from './Counter.tsx'

mountCompiled(Counter, document.querySelector('#app')!)

Then run pnpm vite for development or pnpm vite build for a static production build.

[!NOTE] The compiler and runtime share a protocol version. Always upgrade @vidact/runtime, @vidact/vite, and @vidact/start together. Mixed versions fail loudly at startup rather than silently misbehaving.

Opt-in features

Suspense, transitions, form actions, and retained UI are part of React's API and all work as expected, but they stay out of your bundle until you enable them:

vidact({ features: ['async', 'concurrent'] })
Feature What it enables
async Suspense, lazy, and use(promise)
concurrent useTransition, startTransition, useDeferredValue, flushSync
actions Function-valued form action, useActionState, useOptimistic, useFormStatus
retained-ui Activity for hiding UI while keeping its state

Using a feature that is not enabled produces a compile error naming the flag.

Full-stack with Vidact Start

Swap the plugin for vidactStart() and add a src/routes directory. Each route module exports a Route with an optional server loader; its result reaches the component as typed loaderData.

// src/routes/index.tsx
import { defineFileRoute, type RouteComponentProps } from '@vidact/start'

const loader = () => ({ greeting: 'Hello from the server' })

export function HomeRoute({ loaderData }: RouteComponentProps>) {
  return 
{loaderData.greeting}

}

export const Route = defineFileRoute({ loader, component: HomeRoute })

The server renders HTML with the loader's data, embeds a snapshot, and the client hydrates the existing DOM without rebuilding it. See the @vidact/start README and the Start guides for entries, navigation, data loading, and deployment.

How it works

React source
  -> React Compiler analysis in Rust (AST, scope, HIR/CFG/SSA, dependencies)
  -> Vidact analysis adapter
  -> Vidact static updater IR
  -> Vanilla DOM codegen
  -> @vidact/runtime
  1. @vidact/vite sends untouched TSX to @vidact/compiler, a prebuilt native Node-API addon. Consumers never need Rust or Cargo.
  2. The Rust compiler runs a vendored React Compiler analysis, lowers a static updater graph, and rewrites state, scalar, branch, and keyed-list expressions.
  3. OXC prints the transformed module and lowers JSX through @vidact/runtime/jsx-runtime.
  4. At runtime the component constructs its DOM once. A state write marks a compiler-assigned source dirty. The compiler emits known updaters in execution order with static read/write masks; when runtime-owned capabilities add or remove an updater, the scope composes those declared masks into a cached order. The browser never observes reads or diffs a tree.

React Compiler is an analysis dependency, not Vidact's renderer or code generator. Its internal types terminate at a narrow adapter, and the rest of Vidact uses its own stable facts and IR. The architecture notes record these decisions and the analysis boundary explains the integration constraints.

Reachable source-published packages that declare React in their metadata are qualified for compilation automatically. The current evidence certifies named Base UI paths at compile and SSR time, published Button behavior and DOM ownership in three browsers, and the Base UI-backed components used by the Shop example. It is not a package-wide or shadcn-registry guarantee; the compatibility evidence lists each tested surface.

Packages

Package Role
vidact Project generator with spa, start, and nitro templates
@vidact/runtime Fine-grained direct-DOM runtime: scheduler, state slots, roots, keyed ranges
@vidact/vite Vite plugin that compiles .tsx files and resolves react imports
@vidact/react-types JSX and hook types describing what Vidact actually does, built on @types/react
@vidact/start File routes, loaders, SSR, hydration, and client navigation
@vidact/compiler Native compiler bindings and the vidactc CLI, for tooling authors
@vidact/test-support act and DOM mutation assertions for Vitest browser tests

The Rust side lives in crates/vidact-compiler (analysis facts and updater IR) and crates/vidact-node (the Node-API adapter).

Examples

Every example is ordinary React-shaped TSX. Run them from the repository root after the development setup.

Example Command Highlights
TodoMVC pnpm dev:todomvc Array state, keyed lists, and events with no Virtual DOM
Shop pnpm dev:shop Streaming SSR, "use client" boundaries, Suspense, Tailwind, and local shadcn wrappers over tested Base UI paths
Start pnpm dev:start Nested layouts, typed loaders, dynamic params, route endpoints, hydration
Docs pnpm dev:docs The documentation site itself, built with Vidact Start and headless Fumadocs, deployed on Nitro

The user documentation is written in examples/docs/content/docs: a quick start, a Learn section covering one concept per page, Vidact Start guides, a migration guide from React, a testing guide, and per-package references.

Development

Requirements: Rust 1.96, Node.js 24+, pnpm 10, and Playwright's Chromium, Firefox, and WebKit installs.

scripts/prepare-oxc.sh   # initialize the pinned Oxc submodule and apply the React Compiler patch
pnpm install
pnpm build:packages
pnpm typecheck
cargo test --workspace
pnpm test:browser

pnpm check runs everything CI runs: lint and format gates, type checks, the Rust suite, the cross-browser corpus, package and example verification, production size budgets, and compiler and runtime benchmarks. The benchmark methodology records workloads, sampling, environments, and regression thresholds.

[!TIP] Ordinary builds do not need Go. Only maintainers editing the checked-in Oxc patch install git-go-patch with go install github.com/microsoft/go-infra/cmd/[email protected]. See patched Oxc submodule.

Every pull request needs a changeset. Run pnpm changeset for a published package change,

Issues· 3 开放

查看全部 Issues在 GitHub 打开

暂无开放 Issues,或尚未同步最近议题。

> 标签

TypeScript

暂无评论,来聊聊你的看法吧

> 工具信息

发布日期2026年8月1日
最后更新2026年9月17日
分类编程语言
定价开源

> 相关工具

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言