Skip to content
Backend
Skill

/typescript-client

SpacetimeDB TypeScript/React client SDK reference. Use when building web clients that connect to SpacetimeDB.

From plugin
spacetimedb
26k11 skills1 MCP

Claude Code

$ npx -y skills add clockworklabs/spacetimedb --skill typescript-client --agent claude-code

SKILL.md

typescript-client.SKILL.md
name: typescript-client
description: SpacetimeDB TypeScript/React client SDK reference. Use when building web clients that connect to SpacetimeDB.
license: Apache-2.0
metadata:
  author: clockworklabs
  version: "2.0"
  role: client
  language: typescript
  cursor_globs: "**/*.tsx,**/*.ts"
  cursor_always_apply: true

SpacetimeDB TypeScript Client

Generated bindings convert snake_case names to camelCase, including row fields: a server column `trip_id` is `tripId` on client rows.

React: main.tsx

import React, { useMemo } from 'react';
import ReactDOM from 'react-dom/client';
import { SpacetimeDBProvider } from 'spacetimedb/react';
import { DbConnection } from './module_bindings';
import { MODULE_NAME, SPACETIMEDB_URI } from './config';
import App from './App';

const TOKEN_KEY = `${SPACETIMEDB_URI}/${MODULE_NAME}/auth_token`;

function Root() {
  const connectionBuilder = useMemo(() =>
    DbConnection.builder()
      .withUri(SPACETIMEDB_URI)
      .withDatabaseName(MODULE_NAME)
      .withToken(localStorage.getItem(TOKEN_KEY) ?? undefined)
      .withAutomaticReconnect()
      .onConnect((_conn, _identity, token) => localStorage.setItem(TOKEN_KEY, token)),
    []
  );
  return (
    <SpacetimeDBProvider connectionBuilder={connectionBuilder}>
      <App />
    </SpacetimeDBProvider>
  );
}

ReactDOM.createRoot(document.getElementById('root')!).render(<Root />);

React: App.tsx

import { useTable, useSpacetimeDB } from 'spacetimedb/react';
import { DbConnection, tables } from './module_bindings';

export default function App() {
  const { isActive, identity: myIdentity, getConnection } = useSpacetimeDB();
  const conn = getConnection() as DbConnection | null;

  // useTable owns subscriptions and reports readiness after replay.
  const [entities, entitiesReady] = useTable(tables.entity);
  const [records, recordsReady] = useTable(tables.record);

  const [onlineUsers] = useTable(
    tables.entity.where(r => r.active.eq(true)),
    {
      onInsert: user => console.log('User connected:', user.name),
      onDelete: user => console.log('User disconnected:', user.name),
      onUpdate: (oldUser, newUser) => console.log('Updated:', newUser.name),
    }
  );

  const addRecord = (data: string) => {
    if (!conn || !isActive) return;
    void conn.reducers.addRecord({ data }).catch(console.error);
  };
  const ownsEntity = entities.some(
    row => row.owner.toHexString() === myIdentity?.toHexString()
  );

  return (
    <main>
      <p>{isActive ? 'Connected' : 'Not connected'}</p>
      <p>{entitiesReady && recordsReady ? 'Data ready' : 'Waiting for current data'}</p>
      <p>{onlineUsers.length} users online, {records.length} records</p>
      <p>{ownsEntity ? 'You own an entity' : 'No owned entity'}</p>
      <button disabled={!isActive} onClick={() => addRecord('Hello!')}>
        Add record
      </button>
    </main>
  );
}

Vanilla (non-React)

import { DbConnection, tables } from './module_bindings';

const HOST = 'wss://maincloud.spacetimedb.com';
const DATABASE = 'my_module';
const TOKEN_KEY = `${HOST}/${DATABASE}/auth_token`;

const conn = DbConnection.builder()
  .withUri(HOST)
  .withDatabaseName(DATABASE)
  .withToken(localStorage.getItem(TOKEN_KEY) ?? undefined)
  .withAutomaticReconnect()
  .onConnect((_conn, identity, token) => {
    localStorage.setItem(TOKEN_KEY, token);
    console.log('Connected as:', identity.toHexString());
  })
  .onDisconnect((_ctx, error, nextAttempt, delayMs) => {
    if (nextAttempt !== undefined) {
      console.warn(`Reconnect attempt ${nextAttempt} in ${delayMs} ms`, error);
    } else {
      console.log('Connection ended', error);
    }
  })
  .onConnectError((_ctx, error, nextAttempt, delayMs) => {
    console.error('Connection failed:', error);
    if (nextAttempt !== undefined) {
      console.log(`Retry ${nextAttempt} in ${delayMs} ms`);
    }
  })
  .build();

// Register once; the SDK replays this subscription after reconnecting.
const subscription = conn.subscriptionBuilder()
  .onApplied(() => console.log('Ready'))
  .subscribe([tables.user, tables.message]);

// Row callbacks
conn.db.user.onInsert((ctx, user) => console.log('Joined:', user.name));
conn.db.user.onDelete((ctx, user) => console.log('Left:', user.name));
conn.db.user.onUpdate((ctx, oldUser, newUser) => console.log('Updated:', newUser.name));

Submodule Accessors and View Arguments

Generated bindings preserve submodule accessors as nested members. For a namespace accessor `myAuth` pinned to canonical `auth_data`:

conn.subscriptionBuilder().subscribe([tables.myAuth.users]);
// Rows: conn.db.myAuth.users
// Reducers: conn.reducers.myAuth.verifyToken({ token: 'example' })
// React: useTable(tables.myAuth.users), useReducer(reducers.myAuth.verifyToken)

Use generated accessors in SDK calls; they encode canonical names automatically. Raw SQL/HTTP/CLI calls instead use canonical qualified names such as `auth_data.users` or `auth_data.verify_token`. Namespaces share the connection and cache; they are not separate database connections.

For views with arguments (2.13+), use a raw SQL subscription; TypeScript has no typed argument-bearing view query builder yet:

conn.subscriptionBuilder().subscribe('SELECT * FROM by_id(42)');

Use explicit queries for every table on that connection, not subscribe-all, which skips views with arguments. Only `SELECT * FROM view_name(literal_args)` is supported. All argument values share one cache table; unsubscribe from obsolete arguments to avoid combining their rows. Validate/encode dynamic values as SQL literals rather than concatenating untrusted input. In React, raw SQL subscriptions need explicit lifetime management and cleanup; `useTable` does not supply an argument-bearing view builder.

Automatic Reconnect and Token Refresh

The React provider's connection manager enables automatic reconnect; the explicit `.withAutomati

Read more
@clockworklabs
Ships withspacetimedb

Development at the speed of light

26k11 skills1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
25.5k
Star history of clockworklabs/spacetimedb on GitHub, a chart by star-history.com

Other skills on spacetimedb.

cli

cli

SpacetimeDB CLI reference for initializing projects, building modules, publishing databases, querying data, and managing servers

concepts

concepts

Understand SpacetimeDB architecture and core concepts. Use when learning SpacetimeDB or making architectural decisions.

csharp-client

csharp-client

SpacetimeDB C#/.NET client SDK reference. Use when building C# clients that connect to SpacetimeDB (console, desktop, or any .NET…

mcp

mcp

Operate a running SpacetimeDB database through MCP tools rather than the CLI - list databases, read schemas, run SQL, and call…

More skills from other plugins.

backend

backend

Use this skill when contributing to InsForge's backend package. This is for maintainers editing backend routes, services,…

@insforge@insforgeInstall Skill
exa-agent

exa-agent

Use Exa Agent for multi-step web research, list-building, enrichment, structured output, run continuation, and coverage…

exa11d5.2k
@exa-labs@exa-labsInstall Skill
fde-ontology

fde-ontology

多源输入→本体检出→AI审核→场景升级→代码生成 自动化管线

dgiot4w4.9k
@dgiot@dgiotInstall Skill

Questions about typescript-client.

Which coding agents can use typescript-client?
typescript-client installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.