React adapter

May 29, 2026 · View on GitHub

@reearth/zushi/react ties a plugin to a React component's lifecycle. react is an optional peer dependency.

PluginView

PluginView mounts one element and hosts a surface named "ui" in it (rename via the surface prop; declare extra off-screen surfaces via surfaces). It takes the same options as Plugin plus a few view props (surface, autoResize, className, style, …).

import { PluginView } from "@reearth/zushi/react";
import { quickjs } from "@reearth/zushi";

function MyPlugin() {
  return (
    <PluginView
      backend={quickjs()}
      code={pluginSource}
      style={{ width: 320, height: 240 }}
      exposed={({ surfaces }) => ({ reearth: { ui: surfaces.ui.api } })}
    />
  );
}

usePlugin

The hook directly, when you want to own the element:

import { usePlugin } from "@reearth/zushi/react";
import { quickjs } from "@reearth/zushi";

function MyPlugin() {
  const { containerRef, getPlugin } = usePlugin({
    backend: quickjs(),
    code: pluginSource
  });
  return <div ref={containerRef} />;
}

usePlugin (re)initializes the VM when code/src change and disposes on unmount; other options are read via a ref so they don't force a re-init. getPlugin() returns the live Plugin (e.g. to call handleMessage).

useEmit

useEmit helps push host→plugin messages with stable identity semantics tied to the function-fingerprint machinery (see Storage & events).

React-library compatibility (experimental)

The in-VM JSX runtime is React-like, not React. Third-party libraries that import from "react" won't bind to it by default. As a best-effort escape hatch, alias your bundler's "react" to @reearth/zushi/react-compat (and set jsxImportSource to "@reearth/zushi"); headless libraries that only use hooks and elements may then run inside the VM. Anything touching react-dom, real DOM refs, portals, or concurrent features will not work — treat this as a subset, not full compatibility.