Getting Started
Build your first Uniview plugin in 5 minutes
This guide walks you through creating a simple interactive plugin and rendering it in a Svelte host.
Prerequisites
- Node.js 18+
- pnpm (recommended) or npm
Installation
Install the core packages:
# For React plugin development
pnpm add @uniview/react-runtime @uniview/react-renderer @uniview/protocol react
# For Solid plugin development
pnpm add @uniview/solid-runtime @uniview/solid-renderer @uniview/protocol solid-js
# For host development (Svelte)
pnpm add @uniview/host-sdk @uniview/host-svelte @uniview/protocolCreating a Plugin
Plugins can be written in React or Solid. This guide shows the React path — see below for Solid.
1. Write Your React Component
Create a React component. Plugins can use standard React hooks, state, and standard HTML elements.
import { useState } from "react";
export default function App() {
const [name, setName] = useState("");
const [submitted, setSubmitted] = useState(false);
// Conditional rendering
if (submitted) {
return (
<div className="p-4 border rounded-lg bg-green-50">
<h1 className="text-xl font-bold text-green-700">Welcome, {name}!</h1>
<p className="text-green-600 mt-2">Your registration was successful.</p>
<button
onClick={() => {
setSubmitted(false);
setName("");
}}
className="mt-4 px-4 py-2 bg-white border border-green-200 text-green-700 rounded hover:bg-green-50 transition-colors"
>
Reset
</button>
</div>
);
}
return (
<div className="p-4 border rounded-lg space-y-4 bg-white">
<h1 className="text-xl font-bold">Registration Plugin</h1>
<div className="flex flex-col gap-2">
<label className="text-sm font-medium text-gray-700">
Enter your name:
</label>
<input
value={name}
onChange={(e) => setName(e.target.value)}
className="border p-2 rounded focus:ring-2 focus:ring-blue-500 outline-none"
placeholder="John Doe"
/>
</div>
<button
onClick={() => setSubmitted(true)}
disabled={!name.trim()}
className="w-full px-4 py-2 bg-blue-600 text-white rounded font-medium hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
Submit
</button>
</div>
);
}2. Create the Worker Entry Point
Bootstrap your plugin using the runtime:
import { startWorkerPlugin } from "@uniview/react-runtime";
import App from "./App";
startWorkerPlugin({ App });3. Build the Plugin
Bundle for browser (Web Worker target):
// Using Bun
await Bun.build({
entrypoints: ["./src/worker.ts"],
outdir: "./dist",
target: "browser",
format: "esm",
minify: true,
});Or with other bundlers (esbuild, Vite, etc.):
esbuild src/worker.ts --bundle --format=esm --outfile=dist/plugin.jsThe plugin bundle must be self-contained with all dependencies included. Web Workers can't access the host's modules.
Creating a Solid Plugin
The same steps apply for Solid, with different packages and a required build step:
import { createSignal } from "solid-js";
const App = () => {
const [count, setCount] = createSignal(0);
return (
<div className="p-4">
<p>Count: {count()}</p>
<button onClick={() => setCount((c) => c + 1)}>Increment</button>
</div>
);
};
export default App;import { startSolidWorkerPlugin } from "@uniview/solid-runtime";
import App from "./App";
startSolidWorkerPlugin({ App });Solid plugins require a Babel build step with babel-preset-solid (generate: "universal") because Bun/Node can't natively run Solid's JSX transform. See examples/plugin-solid-example/build.ts for the full build configuration.
When building Solid plugins for Bun/Node.js (target: "bun"), you must add
conditions: ["browser"] to the build config. Without this, Bun resolves
solid-js to its SSR build which has no reactivity.
Creating a Host (Svelte)
1. Set Up the Controller
<script lang="ts">
import { PluginHost } from '@uniview/host-svelte';
import { createWorkerController, createComponentRegistry } from '@uniview/host-sdk';
import type { Component } from 'svelte';
// Create component registry (optional: for custom components)
const registry = createComponentRegistry<Component>();
// Create controller pointing to your plugin
const controller = createWorkerController({
pluginUrl: '/plugins/plugin.js'
});
</script>
<div class="p-8 max-w-2xl mx-auto">
<h1 class="text-2xl font-bold mb-8">Uniview Host Demo</h1>
<div class="border rounded-xl shadow-sm overflow-hidden">
<PluginHost {controller} {registry}>
{#snippet loading()}
<div class="p-8 text-center text-gray-500 animate-pulse">
Loading plugin environment...
</div>
{/snippet}
</PluginHost>
</div>
</div>2. Serve the Plugin
Place your built plugin in static/plugins/ (for SvelteKit) or configure your dev server to serve it.
3. Run the App
pnpm devYour plugin should now render inside the host!
Custom Components
By default, plugins can use HTML elements (div, button, input, etc.). For custom components:
Plugin Side
Create wrapper components that emit custom types:
import { createElement } from "react";
export function Button({ title, onClick, variant = "primary" }) {
return createElement("Button", { title, onClick, variant });
}Host Side
Register renderers for custom types:
<script lang="ts">
import { Button } from '$lib/components/ui/button';
let { title, onclick, variant }: Props = $props();
</script>
<Button {variant} on:click={onclick}>
{title}
</Button>// In your page
registry.register("Button", PluginButton);Runtime Modes
Uniview supports three runtime modes:
| Mode | Use Case | Security |
|---|---|---|
| Worker | Production, untrusted plugins | Sandboxed |
| WebSocket | Server-side plugins, Node.js APIs | Network isolated |
| Main Thread | Development, debugging | None |
For complex setups involving multiple modes, check out the Advanced Host Guide.