There are two distinct dimensions: embedding the frontend into your app, and embedding blit server into your own service.
@blit-sh/react and @blit-sh/solid are workspace-first. Both are thin wrappers over @blit-sh/core's BlitTerminalSurface. A BlitWorkspace owns connections, each connection owns terminals, and each BlitTerminal renders a terminal by ID.
import {
BlitTerminal,
BlitWorkspaceProvider,
useBlitFocusedSession,
useBlitSessions,
useBlitWorkspace,
} from "@blit-sh/react";
import { BlitWorkspace, WebSocketTransport } from "@blit-sh/core";
import { useEffect, useMemo } from "react";
function EmbeddedBlit({ wasm, passphrase }: { wasm: any; passphrase: string }) {
const transport = useMemo(
() => new WebSocketTransport("wss://example.com/blit", passphrase),
[passphrase],
);
const workspace = useMemo(
() =>
new BlitWorkspace({
wasm,
connections: [{ id: "default", transport }],
}),
[transport, wasm],
);
useEffect(() => () => workspace.dispose(), [workspace]);
return (
<BlitWorkspaceProvider workspace={workspace}>
<TerminalScreen />
</BlitWorkspaceProvider>
);
}
function TerminalScreen() {
const workspace = useBlitWorkspace();
const sessions = useBlitSessions();
const focusedSession = useBlitFocusedSession();
useEffect(() => {
if (sessions.length > 0) return;
void workspace.createSession({
connectionId: "default",
rows: 24,
cols: 80,
});
}, [sessions.length, workspace]);
return (
<BlitTerminal
sessionId={focusedSession?.id ?? null}
style={{ width: "100%", height: "100vh" }}
/>
);
}| API | Purpose |
|---|---|
new BlitWorkspace({ wasm, connections }) |
Create a workspace with one or more transports |
BlitWorkspaceProvider |
Put the workspace, palette, and font settings in context |
useBlitWorkspace() |
Get the imperative workspace object |
useBlitWorkspaceState() |
Read the full reactive workspace snapshot |
useBlitConnection(connectionId?) |
Read one connection snapshot |
useBlitSessions() |
Read all terminals |
useBlitFocusedSession() |
Read the currently focused terminal |
useBlitWorkspaceConnection(workspace, id, transport) |
Manage a connection lifecycle with cleanup |
BlitTerminal |
Render one terminal by sessionId |
import {
BlitTerminal,
BlitWorkspaceProvider,
createBlitWorkspace,
createBlitWorkspaceState,
createBlitSessions,
useBlitFocusedSession,
} from "@blit-sh/solid";
import { BlitWorkspace } from "@blit-sh/core";
import { createSignal, onCleanup, createEffect } from "solid-js";
function EmbeddedBlit(props: { wasm: any; passphrase: string }) {
const workspace = new BlitWorkspace({
wasm: props.wasm,
connections: [
{
id: "default",
transport: {
type: "websocket",
url: "wss://example.com/blit",
passphrase: props.passphrase,
},
},
],
});
onCleanup(() => workspace.dispose());
return (
<BlitWorkspaceProvider workspace={workspace}>
<TerminalScreen />
</BlitWorkspaceProvider>
);
}
function TerminalScreen() {
const workspace = createBlitWorkspace();
const sessions = createBlitSessions();
const focusedSession = () => useBlitFocusedSession(workspace);
createEffect(() => {
if (sessions().length > 0) return;
workspace.createSession({ connectionId: "default", rows: 24, cols: 80 });
});
return (
<BlitTerminal
sessionId={focusedSession()?.id ?? null}
style={{ width: "100%", height: "100vh" }}
/>
);
}| API | Purpose |
|---|---|
new BlitWorkspace({ wasm, connections }) |
Create a workspace with one or more transports |
BlitWorkspaceProvider |
Put the workspace, palette, and font settings in context |
createBlitWorkspace() |
Get the imperative workspace object from context |
createBlitWorkspaceState(workspace?) |
Reactive signal tracking the workspace snapshot |
createBlitSessions(workspace?) |
Reactive signal tracking all terminals |
useBlitSession(workspace, sessionId) |
Look up a single terminal by ID (non-reactive) |
useBlitFocusedSession(workspace) |
Look up the focused terminal (non-reactive) |
useBlitConnection(workspace, sessionId) |
Look up a connection snapshot (non-reactive) |
createBlitWorkspaceConnection(workspace, id, transport) |
Manage a connection lifecycle with onCleanup |
BlitTerminal |
Render one terminal by sessionId |
BlitSurfaceView renders a single Wayland surface from a terminal's compositor. The server encodes each surface as H.264 or AV1; the component decodes via WebCodecs and draws to a canvas.
import { BlitSurfaceView } from "@blit-sh/react";
function AppWindow({
connectionId,
surfaceId,
}: {
connectionId: string;
surfaceId: number;
}) {
return (
<BlitSurfaceView
connectionId={connectionId}
surfaceId={surfaceId}
style={{ width: 800, height: 600 }}
/>
);
}Every terminal has an experimental Wayland compositor available. Any command — shell, TUI, or GUI — can open Wayland surfaces:
workspace.createSession({
connectionId: "default",
rows: 24,
cols: 80,
command: "my-gui-app",
});Surfaces created by the terminal appear in the connection's surfaceStore, keyed by the terminal's PTY ID. Each surface has a surfaceId, parentId, title, appId, width, and height.
createSession({ connectionId, rows, cols, tag?, command?, cwdFromSessionId? })closeSession(sessionId)restartSession(sessionId)focusSession(sessionId | null)search(query, { connectionId? })setVisibleSessions(sessionIds)addConnection(...)/removeConnection(connectionId)/reconnectConnection(connectionId)
All transports share a common set of options (BlitTransportOptions):
| Option | Default | Description |
|---|---|---|
reconnect |
true |
Auto-reconnect on disconnect |
reconnectDelay |
500 |
Initial reconnect delay (ms) |
maxReconnectDelay |
10000 |
Maximum reconnect delay (ms) |
reconnectBackoff |
1.5 |
Backoff multiplier |
connectTimeoutMs |
none (WS) / 10000 (WT, WebRTC) |
Connection timeout (ms) |
// WebSocket
new WebSocketTransport(url, passphrase, { reconnect, reconnectDelay, connectTimeoutMs, ... })
// WebTransport (QUIC/HTTP3)
new WebTransportTransport(url, passphrase, { serverCertificateHash, ... })
// WebRTC data channel
createWebRtcDataChannelTransport(peerConnection, { label, displayRateFps, ... })Or implement your own:
interface BlitTransport {
connect(): void;
send(data: Uint8Array): void;
close(): void;
readonly status: ConnectionStatus;
readonly authRejected: boolean;
readonly lastError: string | null;
addEventListener(type: "message" | "statuschange", listener: Function): void;
removeEventListener(
type: "message" | "statuschange",
listener: Function,
): void;
}fd-channel lets an external process own blit server's lifecycle and control which clients connect via SCM_RIGHTS fd passing. See ARCHITECTURE.md for the protocol details and the working examples: