A universal plugin system for React plugins that render in any host framework.
Uniview enables writing plugins in React that can be rendered by Svelte, Vue, React, or any other framework. Plugins run in isolated environments (Web Workers, Node.js, Deno, Bun) and communicate with hosts via RPC.
RPC (kkrpc)
┌──────────────────┐ ◄──────────────────► ┌──────────────────┐
│ Plugin (React) │ UINode tree │ Host (Svelte) │
│ Web Worker │ │ or Vue, React │
└──────────────────┘ └──────────────────┘
Key Features:
| Package | Description |
|---|---|
| @uniview/protocol | Core types, UINode schema, RPC interfaces |
| @uniview/react-renderer | Custom React reconciler producing UINode trees |
| @uniview/runtime | Plugin bootstrap for Worker/WebSocket environments |
| @uniview/host-sdk | Framework-agnostic host controller |
| @uniview/host-svelte | Svelte 5 rendering adapter |
The fastest way to see Uniview in action:
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Run the complete example (bridge + plugins + host)
cd examples/host-svelte-demo
pnpm dev:all
Then open http://localhost:5173 and try both Worker and Node.js modes.
Create a React component and bootstrap it with the runtime:
// worker.ts
import { startWorkerPlugin } from "@uniview/runtime";
import App from "./App";
startWorkerPlugin({ App });
// App.tsx
import { useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
return (
<div className="p-4">
<p>Count: {count}</p>
<button onClick={() => setCount((c) => c + 1)}>Increment</button>
</div>
);
}
Load and render the plugin:
<script lang="ts">
import { PluginHost } from '@uniview/host-svelte';
import { createWorkerController, createComponentRegistry } from '@uniview/host-sdk';
const registry = createComponentRegistry();
const controller = createWorkerController({
pluginUrl: '/plugins/my-plugin.js'
});
</script>
<PluginHost {controller} {registry}>
{#snippet loading()}
<p>Loading plugin...</p>
{/snippet}
</PluginHost>
┌─────────────────┐ ┌─────────────────┐
│ Browser Host │ ◄─postMessage─►│ Web Worker │
│ (Svelte) │ │ (Plugin) │
└─────────────────┘ └─────────────────┘
For server-side plugins, Uniview uses a Bridge Server pattern:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Browser Host │◄─────►│ Bridge Server │◄─────►│ Plugin Client │
│ (Svelte) │ WS │ (Elysia) │ WS │ (Node.js) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
:5173 :3000 (connects to bridge)
Why Bridge Architecture?
:3000), simplifying deployment.// Plugin connects as client
import { connectToHostServer } from "@uniview/runtime/ws-client";
connectToHostServer({
App: MyPlugin,
serverUrl: "ws://localhost:3000",
pluginId: "my-plugin",
});
// Host connects to same bridge
const controller = createWebSocketController({
serverUrl: "ws://localhost:3000",
pluginId: "my-plugin",
});
| Mode | Environment | Isolation | Use Case |
|---|---|---|---|
| Worker | Browser | Full sandbox | Production, untrusted plugins |
| WebSocket | Node.js/Deno/Bun | Process boundary | Server-side, full runtime access |
| Main Thread | Browser | None | Development only |
// Worker mode (production)
const controller = createWorkerController({
pluginUrl: "/plugins/my-plugin.js",
});
// WebSocket mode (server-side plugins)
const controller = createWebSocketController({
serverUrl: "ws://localhost:3000",
pluginId: "my-plugin",
});
// Main thread mode (development)
import App from "./plugin/App";
const controller = createMainController({ App });
uniview/
├── packages/
│ ├── protocol/ # Shared types and contracts
│ ├── react-renderer/ # Custom React reconciler
│ ├── runtime/ # Plugin bootstrap (worker + ws-client)
│ ├── host-sdk/ # Host controller logic
│ └── host-svelte/ # Svelte 5 adapter
├── examples/
│ ├── host-svelte-demo/ # Example host + bridge server
│ ├── plugin-api/ # Reusable React components
│ └── plugin-example/ # Example plugins
├── vendors/
│ └── kkrpc/ # RPC library (submodule)
└── docs/ # Documentation site
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Run the full example
cd examples/host-svelte-demo
pnpm dev:all # Starts bridge + plugins + host
# Or run components separately:
# Terminal 1: Bridge server
bun server/index.ts
# Terminal 2: Plugin clients
cd ../plugin-example && pnpm client
# Terminal 3: SvelteKit host
pnpm dev
See the docs folder or visit the documentation site for:
Plugin (React)
│
▼
┌─────────────────────┐
│ react-renderer │ Custom reconciler converts React
│ (InternalNode) │ elements to in-memory tree
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ serializeTree() │ Converts to JSON-safe UINode,
│ HandlerRegistry │ replaces functions with IDs
└──────────┬──────────┘
│
▼ RPC (kkrpc)
┌─────────────────────┐
│ host-sdk │ PluginController manages
│ PluginController │ connection and tree updates
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ host-svelte │ ComponentRenderer recursively
│ ComponentRenderer │ renders UINode as Svelte
└─────────────────────┘
Inspired by Raycast's plugin architecture which uses a custom React reconciler to render plugins using native AppKit components. Uniview takes this concept to the web, rendering to any framework.
MIT