Skip to main content

Create a New Extension

First, log in to your Vision account:
vision login
Then scaffold a new extension:
vision init
Interactive mode asks for a name, description, which targets you want (editor panel, OBS layer, interactive page), and whether to include server functions. A ready-to-go project is created in seconds, including initial extension registration.

Project Structure

my-extension/
  package.json
  tsconfig.json
  vision.config.json    # Extension metadata
  src/
    admin.tsx            # Editor panel (settings UI)
    layer.tsx            # OBS overlay output
    interactive.tsx      # Live controls (optional)
  server/                # Only if server functions enabled
    schema.ts            # Database schema
    functions.ts         # Server functions (queries, mutations, actions)
Each src/ file is an independent entry point. You only need to include the targets your extension uses. The server/ directory is created when you opt into server functions during vision init.

Fonts

You can preload Google Fonts for the extension via vision.config.json:
{
  "name": "my-extension",
  "description": "My Vision extension",
  "targets": ["editor", "layer"],
  "server": false,
  "fonts": ["Inter", "Space Mono"]
}
For custom local fonts, add files under public/ and register them with localFont:
import { Text, Vision, localFont } from "@1upvision/sdk";

const scoreFont = localFont({
  src: "/fonts/Orbitron-Bold.woff2",
  family: "Orbitron",
  fallback: ["sans-serif"],
});

function Overlay() {
  return <Text content="GOAL!" style={{ ...scoreFont.style, fontSize: 64 }} />;
}

Vision.render(<Overlay />, { target: "layer" });
localFont accepts paths like /fonts/MyFont.woff2 (recommended) and also normalizes ./public/fonts/MyFont.woff2 to the same output path. When you run vision dev or build for upload, files from public/ are copied to dist/ automatically, so local fonts are shipped with your extension.

Write Your First Extension

Here’s a minimal extension with a settings panel and an OBS overlay:

Editor Panel — src/admin.tsx

The settings UI streamers see in the overlay editor.
import {
  Vision,
  CompactView,
  Text,
  Toggle,
  TextField,
  useExtensionStorage,
} from "@1upvision/sdk";

function Settings() {
  const [storage, setStorage] = useExtensionStorage();

  return (
    <CompactView title="My Extension">
      <Text content="Configure what shows on stream." variant="muted" />
      <Toggle
        label="Visible"
        checked={storage.visible ?? false}
        onChange={(checked) => setStorage({ ...storage, visible: checked })}
      />
      <TextField
        label="Display Text"
        placeholder="Enter text..."
        value={storage.text ?? ""}
        onChange={(value) => setStorage({ ...storage, text: value })}
      />
    </CompactView>
  );
}

Vision.render(<Settings />, { target: "editor" });

OBS Layer — src/layer.tsx

What viewers see on stream. Input components like toggles and text fields render as display-only here.
import { Vision, Text, useExtensionStorage } from "@1upvision/sdk";

function Overlay() {
  const [storage] = useExtensionStorage();

  if (!storage.visible) return null;

  return <Text content={storage.text ?? ""} variant="heading" />;
}

Vision.render(<Overlay />, { target: "layer" });

Interactive Page — src/interactive.tsx (optional)

A dedicated full-screen page for active participants and control workflows. All components are fully interactive here, just like the editor panel.
Interactive pages are not intended as high-scale public viewer pages. Do not use them as a replacement for a general web app serving thousands of concurrent stream viewers.
import {
  Vision,
  CompactView,
  Text,
  Button,
  useExtensionStorage,
} from "@1upvision/sdk";

function Controls() {
  const [storage, setStorage] = useExtensionStorage();

  return (
    <CompactView title="My Extension">
      <Text content="Quick controls for your stream." variant="muted" />
      <Button
        label={storage.visible ? "Hide" : "Show"}
        variant={storage.visible ? "destructive" : "default"}
        onClick={() => setStorage({ ...storage, visible: !storage.visible })}
      />
    </CompactView>
  );
}

Vision.render(<Controls />, { target: "interactive" });
Need a shareable participant link with custom query params? Use useExtensionContext() and start from context.interactiveUrl.
That’s it. The editor configures, the interactive page gives quick actions, and the layer shows the result on stream — all synced in real-time.
Every entry point must call Vision.render() within 10 seconds of loading, or the extension will be terminated.

Build

pnpm install
pnpm build
This compiles each entry point into dist/admin.js, dist/layer.js, etc.
React and @1upvision/sdk are provided by the Vision runtime — they’re not bundled into your extension. Your output stays small.

Upload and Test

1

Create the extension

In Vision, go to Extensions > My Extensions > Create Extension and select Vision SDK as the type.
2

Create a version

Click Create Version and enter a version number (e.g., 1.0.0).
3

Upload your build

Upload your dist/ folder as a zip file.
4

Add to an overlay

Open an overlay in the editor, add a layer, and select your extension. Your settings panel should appear.

Local Development

Use vision dev for the fastest workflow. It watches your project, rebuilds on change, and pushes everything (client bundles, server bundles, and schema) automatically:
vision dev
Alternatively, use Switch to Test mode to load client bundles from a local URL:
npx serve dist --ssl-cert cert.pem --ssl-key key.pem
Then set the test URL to https://localhost:3000 in the extension settings. Changes to your build are picked up on reload.

Testing Server Functions

During development, you can invoke server functions directly from the terminal:
vision run query:getPlayers
vision run mutation:addPlayer '{"name": "Alice", "score": 100}'

Agent Skill (Optional)

If you use coding agents, install the Vision Extensions skill so they have SDK-specific guidance:
npx skills add 1upmanagement/vision-extensions

Next Steps

Server Functions

Add a backend with database, auth, and external APIs

Components

All available UI components and how to use them

Hooks

Storage, context, and real-time sync