Version: 2.0.0
On this page
The SpacetimeDB client SDK for TypeScript contains all the tools you need to build clients for SpacetimeDB modules using Typescript, either in the browser or with NodeJS.
Before diving into the reference, you may want to review:
- Generating Client Bindings - How to generate TypeScript bindings from your module
- Connecting to SpacetimeDB - Establishing and managing connections
- SDK API Reference - Core concepts that apply across all SDKs
| Name | Description |
|---|---|
| Project setup | Configure your TypeScript project to use the SpacetimeDB TypeScript client SDK. |
| Generate module bindings | Use the SpacetimeDB CLI to generate module-specific types and interfaces. |
DbConnection type | A connection to a remote database. |
DbContext interface | Methods for interacting with the remote database. Implemented by DbConnection and various event context types. |
EventContext type | DbContext available in row callbacks. |
ReducerEventContext type | DbContext available in reducer callbacks. |
SubscriptionEventContext type | DbContext available in subscription-related callbacks. |
ErrorContext type | DbContext available in error-related callbacks. |
| Access the client cache | Make local queries against subscribed rows, and register row callbacks to run when subscribed rows change. |
| Observe and invoke reducers | Send requests to the database to run reducers, and register callbacks to run when notified of reducers. |
| React Integration | React hooks and components for SpacetimeDB (spacetimedb/react). |
| Identify a client | Types for identifying users and client connections. |
| Query Builder API | Type-safe query builder for subscriptions using the tables export. |
| Framework Integrations | React, SolidJS, Vue, and Svelte hooks for reactive SpacetimeDB data. |
Project setup
First, create a new client project, and add the following to your
tsconfig.json file:
{
"compilerOptions": {
//You can use any target higher than this one
//https://www.typescriptlang.org/tsconfig#target
"target": "es2015"
}
}
Then add the SpacetimeDB SDK to your dependencies:
cd client
npm install spacetimedb
WARNING! The
@clockworklabs/spacetimedb-sdkpackage has been deprecated in favor of thespacetimedbpackage as of SpacetimeDB version 1.4.0. If you are using the old SDK package, you will need to switch tospacetimedb. You will also need a SpacetimeDB CLI version of 1.4.0+ to generate bindings for the newspacetimedbpackage.
You should have this folder layout starting from the root of your project:
quickstart-chat
├── client
│ ├── node_modules
│ ├── public
│ └── src
└── server
└── srcTip for utilities/scripts
If want to create a quick script to test your module bindings from the command line, you can use https://www.npmjs.com/package/tsx to execute TypeScript files.
Then you create a script.ts file and add the imports, code and execute
with:
npx tsx src/script.tsGenerate module bindings
Each SpacetimeDB client depends on some bindings specific to your
module. Create a module_bindings directory in your project’s src
directory and generate the Typescript interface files using the
Spacetime CLI. From your project directory, run:
mkdir -p client/src/module_bindings
spacetime generate --lang typescript \
--out-dir client/src/module_bindings \
--module-path PATH-TO-MODULE-DIRECTORY
Import the module_bindings in your client’s main file:
import * as moduleBindings from './module_bindings/index';
You may also need to import some definitions from the SDK library:
import { Identity, ConnectionId, Event, ReducerEvent } from 'spacetimedb';Type DbConnection
DbConnection;
A connection to a remote database is represented by the DbConnection
type. This type is generated per-module, and contains information about
the types, tables and reducers defined by your module.
| Name | Description |
|---|---|
| Connect to a database | Construct a DbConnection. |
| Access tables and reducers | Access subscribed rows in the client cache, request reducer invocations, and register callbacks. |
Connect to a database
class DbConnection {
public static builder(): DbConnectionBuilder;
}
Construct a DbConnection by calling DbConnection.builder() and
chaining configuration methods, then calling .build(). You must at
least specify withUri, to supply the URI of the SpacetimeDB to which
you published your module, and withDatabaseName, to supply the
human-readable SpacetimeDB domain name or the raw Identity which
identifies the database.
| Name | Description |
|---|---|
withUri method | Set the URI of the SpacetimeDB instance which hosts the remote database. |
withDatabaseName method | Set the name or Identity of the remote database. |
withConfirmedReads method | Enable or disable confirmed reads. |
onConnect callback | Register a callback to run when the connection is successfully established. |
onConnectError callback | Register a callback to run if the connection is rejected or the host is unreachable. |
onDisconnect callback | Register a callback to run when the connection ends. |
withToken method | Supply a token to authenticate with the remote database. |
build method | Finalize configuration and connect. |
Method withUri
class DbConnectionBuilder {
public withUri(uri: string): DbConnectionBuilder;
}
Configure the URI of the SpacetimeDB instance or cluster which hosts the remote database.
Method withDatabaseName
class DbConnectionBuilder {
public withDatabaseName(name_or_identity: string): DbConnectionBuilder;
}
Configure the SpacetimeDB domain name or hex string encoded Identity
of the remote database which identifies it within the SpacetimeDB
instance or cluster.
Method withConfirmedReads
class DbConnectionBuilder {
public withConfirmedReads(confirmedReads: boolean): DbConnectionBuilder;
}
Configure the connection to request confirmed reads.
When enabled, the server will send query results only after they are
confirmed to be durable, i.e. persisted to disk on one or more replicas
depending on the replication settings of the database. When set to
false, the server will send results as soon as transactions are
committed in memory.
If this method is not called, the server chooses the default.
Callback onConnect
class DbConnectionBuilder {
public onConnect(
callback: (ctx: DbConnection, identity: Identity, token: string) => void
): DbConnectionBuilder;
}
Chain a call to .onConnect(callback) to your builder to register a
callback to run when your new DbConnection successfully initiates its
connection to the remote database. The callback accepts three arguments:
a reference to the DbConnection, the Identity by which SpacetimeDB
identifies this connection, and a private access token which can be
saved and later passed to withToken to
authenticate the same user in future connections.
Callback onConnectError
class DbConnectionBuilder {
public onConnectError(
callback: (ctx: ErrorContext, error: Error) => void
): DbConnectionBuilder;
}
Chain a call to .onConnectError(callback) to your builder to register
a callback to run when your connection fails.
Callback onDisconnect
class DbConnectionBuilder {
public onDisconnect(
callback: (ctx: ErrorContext, error: Error | null) => void
): DbConnectionBuilder;
}
Chain a call to .onDisconnect(callback) to your builder to register a
callback to run when your DbConnection disconnects from the remote
database, either as a result of a call to
disconnect or due to an error.
Method withToken
class DbConnectionBuilder {
public withToken(token?: string): DbConnectionBuilder;
}
Chain a call to .withToken(token) to your builder to provide an OpenID
Connect compliant JSON Web Token to authenticate with, or to explicitly
select an anonymous connection. If this method is not called or null
is passed, SpacetimeDB will generate a new Identity and sign a new
private access token for the connection.
Method build
class DbConnectionBuilder {
public build(): DbConnection;
}
After configuring the connection and registering callbacks, attempt to open the connection.
Access tables and reducers
Field db
class DbConnection {
public db: RemoteTables;
}
The db field of the DbConnection provides access to the subscribed
view of the remote database’s tables. See Access the client
cache.
Field reducers
class DbConnection {
public reducers: RemoteReducers;
}
The reducers field of the DbConnection provides access to reducers
exposed by the remote module. See Observe and invoke
reducers.
Interface DbContext
interface DbContext<
DbView,
Reducers,
>
DbConnection,
EventContext,
ReducerEventContext,
SubscriptionEventContext and
ErrorContext all implement DbContext.
DbContext has fields and methods for inspecting and configuring your
connection to the remote database.
The DbContext interface is implemented by connections and contexts to
every module. This means that its DbView and
Reducers are generic types.
| Name | Description |
|---|---|
db field | Access subscribed rows of tables and register row callbacks. |
reducers field | Request reducer invocations and register reducer callbacks. |
disconnect method | End the connection. |
| Subscribe to queries | Register subscription queries to receive updates about matching rows. |
| Read connection metadata | Access the connection’s Identity and ConnectionId |
Field db
interface DbContext {
db: DbView;
}
The db field of a DbContext provides access to the subscribed view
of the remote database’s tables. See Access the client
cache.
Field reducers
interface DbContext {
reducers: Reducers;
}
The reducers field of a DbContext provides access to reducers
exposed by the remote module. See Observe and invoke
reducers.
Method disconnect
interface DbContext {
disconnect(): void;
}
Gracefully close the DbConnection. Throws an error if the connection
is already disconnected.
Subscribe to queries
| Name | Description |
|---|---|
SubscriptionBuilder type | Builder-pattern constructor to register subscribed queries. |
SubscriptionHandle type | Manage an active subscription. |
Type SubscriptionBuilder
SubscriptionBuilder;| Name | Description |
|---|---|
ctx.subscriptionBuilder() constructor | Begin configuring a new subscription. |
onApplied callback | Register a callback to run when matching rows become available. |
onError callback | Register a callback to run if the subscription fails. |
subscribe method | Finish configuration and subscribe to one or more queries. |
subscribeToAllTables method | Convenience method to subscribe to the entire database. |
Constructor ctx.subscriptionBuilder()
interface DbContext {
subscriptionBuilder(): SubscriptionBuilder;
}
Subscribe to queries by calling ctx.subscriptionBuilder() and chaining
configuration methods, then calling .subscribe(queries).
Callback onApplied
class SubscriptionBuilder {
public onApplied(
callback: (ctx: SubscriptionEventContext) => void
): SubscriptionBuilder;
}
Register a callback to run when the subscription is applied and the matching rows are inserted into the client cache.
Callback onError
class SubscriptionBuilder {
public onError(
callback: (ctx: ErrorContext, error: Error) => void
): SubscriptionBuilder;
}
Register a callback to run if the subscription is rejected or
unexpectedly terminated by the server. This is most frequently caused by
passing an invalid query to subscribe.
Method subscribe
class SubscriptionBuilder {
// Subscribe using raw SQL strings
subscribe(queries: string | string[]): SubscriptionHandle;
// Subscribe using query builders (recommended)
subscribe(query: TableRef | TableRef[]): SubscriptionHandle;
}
Subscribe to a set of queries. Use query builders as the default for type-safe, auto-completing queries. Raw SQL strings are also supported for advanced use cases.
import { tables } from './module_bindings';
// Query builder (recommended) — type-safe, auto-completing
conn.subscriptionBuilder().subscribe(tables.user);
conn.subscriptionBuilder().subscribe([tables.user, tables.message]);
conn.subscriptionBuilder().subscribe(
tables.user.where(r => r.online.eq(true))
);
For raw SQL subscription syntax, see the SpacetimeDB SQL Reference.
Method subscribeToAllTables
class SubscriptionBuilder {
subscribeToAllTables(): void;
}
Subscribe to all rows from all public tables, including public event
tables. This method is provided as a convenience for simple clients. The
subscription initiated by subscribeToAllTables cannot be canceled
after it is initiated. You should subscribe to specific
queries if you need fine-grained control over the
lifecycle of your subscriptions.
Query Builder API
The TypeScript SDK provides a type-safe query builder as the recommended way to define subscriptions. Query builders give you auto-completion and compile-time type checking.
The tables export
Your generated module_bindings exports a tables object. Each
property on tables corresponds to a public table in your module. These
table refs serve double duty: they are both table references and query
builders.
import { tables } from './module_bindings';
// `tables.user` selects all rows from `user`
// `tables.message` selects all rows from `message`Building queries with where
Call .where() on a table ref to add a filter. The predicate function
receives a row expression with typed column accessors:
// All users
tables.user
// Online users only
tables.user.where(r => r.online.eq(true))
// Users with a specific name
tables.user.where(r => r.name.eq('Alice'))Column operators
Each column accessor on the row expression supports the following comparison operators:
| Operator | Description | Example |
|---|---|---|
eq | Equal to | r.online.eq(true) |
ne | Not equal to | r.name.ne('Anonymous') |
lt | Less than | r.age.lt(18) |
lte | Less than or equal to | r.level.lte(5) |
gt | Greater than | r.score.gt(100) |
gte | Greater than or equal to | r.requiredLevel.gte(10) |
Boolean combinators
Combine multiple conditions using instance methods or standalone functions:
// Instance methods — chain on a boolean expression
tables.user.where(r => r.age.gte(18).and(r.age.lt(65)))
tables.user.where(r => r.online.eq(true).or(r.name.eq('Admin')))
tables.user.where(r => r.online.eq(true).not())
// Standalone functions — useful for combining many conditions
import { and, or, not } from './module_bindings';
tables.user.where(r => and(r.age.gte(18), r.age.lt(65)))
tables.user.where(r => or(r.online.eq(true), r.name.eq('Admin')))
tables.user.where(r => not(r.online.eq(true)))Semijoins
Semijoins match rows across two tables and return rows from one side:
leftSemijoin(...)returns rows from the left side that match at least one row on the right.rightSemijoin(...)returns rows from the right side that match at least one row on the left.- The join predicate is built from indexed row expressions and should compare indexed columns.
- Filters before a semijoin apply to the pre-join source side. Filters after a semijoin apply to the returned side.
const leftSide = tables.player
.where(p => p.score.gte(1000))
.leftSemijoin(tables.playerLevel, (p, pl) => p.id.eq(pl.playerId))
.where(p => p.online.eq(true));
const rightSide = tables.player
.where(p => p.score.gte(1000))
.rightSemijoin(tables.playerLevel, (p, pl) => p.id.eq(pl.playerId))
.where(pl => pl.level.gte(10));Using query builders with subscriptions
Query builders can be passed directly to subscribe:
import { tables } from './module_bindings';
// Subscribe to a single table (all rows)
conn.subscriptionBuilder().subscribe(tables.user);
// Subscribe to a filtered query
conn.subscriptionBuilder().subscribe(
tables.user.where(r => r.online.eq(true))
);
// Subscribe to multiple queries
conn.subscriptionBuilder().subscribe([
tables.user,
tables.message,
]);Type SubscriptionHandle
SubscriptionHandle;
A SubscriptionHandle represents a subscribed query or a group of
subscribed queries.
The SubscriptionHandle does not contain or provide access to the
subscribed rows. Subscribed rows of all subscriptions by a connection
are contained within that connection’s ctx.db. See
Access the client cache.
| Name | Description |
|---|---|
isEnded method | Determine whether the subscription has ended. |
isActive method | Determine whether the subscription is active and its matching rows are present in the client cache. |
unsubscribe method | Discard a subscription. |
unsubscribeThen method | Discard a subscription, and register a callback to run when its matching rows are removed from the client cache. |
Method isEnded
class SubscriptionHandle {
public isEnded(): boolean;
}
Returns true if this subscription has been terminated due to an unsubscribe call or an error.
Method isActive
class SubscriptionHandle {
public isActive(): boolean;
}
Returns true if this subscription has been applied and has not yet been unsubscribed.
Method unsubscribe
class SubscriptionHandle {
public unsubscribe(): void;
}
Terminate this subscription, causing matching rows to be removed from
the client cache. Any rows removed from the client cache this way will
have onDelete callbacks run for them.
Unsubscribing is an asynchronous operation. Matching rows are not
removed from the client cache immediately. Use
unsubscribeThen to run a callback once the
unsubscribe operation is completed.
Throws an error if the subscription has already ended, either due to a
previous call to unsubscribe or
unsubscribeThen, or due to an error.
Method unsubscribeThen
class SubscriptionHandle {
public unsubscribeThen(on_end: (ctx: SubscriptionEventContext) => void): void;
}
Terminate this subscription, and run the onEnd callback when the
subscription is ended and its matching rows are removed from the client
cache. Any rows removed from the client cache this way will have
onDelete callbacks run for them.
Returns an error if the subscription has already ended, either due to a
previous call to unsubscribe or
unsubscribeThen, or due to an error.
Read connection metadata
Field isActive
interface DbContext {
isActive: boolean;
}
true if the connection has not yet disconnected. Note that a
connection isActive when it is constructed, before its onConnect
callback is invoked.
Type EventContext
EventContext;
An EventContext is a DbContext augmented
with a field event: Event. EventContexts are passed
as the first argument to row callbacks onInsert,
onDelete and onUpdate.
| Name | Description |
|---|---|
event field | Enum describing the cause of the current row callback. |
db field | Provides access to the client cache. |
reducers field | Allows requesting reducers run on the remote database. |
Event type | Possible events which can cause a row callback to be invoked. |
Field event
class EventContext {
public event: Event<Reducer>;
}
/* other fields */
The Event contained in the EventContext describes
what happened to cause the current row callback to be invoked.
Field db
class EventContext {
public db: RemoteTables;
}
The db field of the context provides access to the subscribed view of
the remote database’s tables. See Access the client
cache.
Field reducers
class EventContext {
public reducers: RemoteReducers;
}
The reducers field of the context provides access to reducers exposed
by the remote module. See Observe and invoke
reducers.
Type Event
type Event<Reducer> =
| { tag: 'Reducer'; value: ReducerEvent<Reducer> }
| { tag: 'SubscribeApplied' }
| { tag: 'UnsubscribeApplied' }
| { tag: 'Error'; value: Error }
| { tag: 'Transaction' };| Name | Description |
|---|---|
Reducer variant | A reducer ran in the remote database. |
SubscribeApplied variant | A new subscription was applied to the client cache. |
UnsubscribeApplied variant | A previous subscription was removed from the client cache after a call to unsubscribe. |
Error variant | A previous subscription was removed from the client cache due to an error. |
Transaction variant | A transaction ran in the remote database, but was not attributed to a known reducer. |
ReducerEvent type | Metadata about a reducer run. Contained in Event::Reducer and ReducerEventContext. |
UpdateStatus type | Completion status of a reducer run. |
Reducer type | Module-specific generated enum with a variant for each reducer defined by the module. |
Variant Reducer
{
tag: 'Reducer';
value: ReducerEvent<Reducer>;
}
Event when we are notified that a reducer ran in the remote database.
The ReducerEvent contains metadata about the
reducer run, including its arguments and termination
status(#type-updatestatus).
This event is passed to row callbacks resulting from modifications by the reducer.
Variant SubscribeApplied
{
tag: 'SubscribeApplied';
}
Event when our subscription is applied and its rows are inserted into the client cache.
This event is passed to row onInsert callbacks
resulting from the new subscription.
Variant UnsubscribeApplied
{
tag: 'UnsubscribeApplied';
}
Event when our subscription is removed after a call to
SubscriptionHandle.unsubscribe or
SubscriptionHandle.unsubscribeThen and its
matching rows are deleted from the client cache.
This event is passed to row onDelete callbacks
resulting from the subscription ending.
Variant Error
{
tag: 'Error';
value: Error;
}
Event when a subscription ends unexpectedly due to an error.
This event is passed to row onDelete callbacks
resulting from the subscription ending.
Variant Transaction
{
tag: 'Transaction';
}
Event when we are notified of a transaction in the remote database which we cannot associate with a known reducer. This may be an ad-hoc SQL query or a reducer for which we do not have bindings.
This event is passed to row callbacks resulting from modifications by the transaction.
Type ReducerEvent
A ReducerEvent contains metadata about a reducer run.
type ReducerEvent<Reducer> = {
/**
* The time when the reducer started running.
*/
timestamp: Timestamp;
/**
* Whether the reducer committed, was aborted due to insufficient energy, or failed with an error message.
*/
status: UpdateStatus;
/**
* The identity of the caller.
* TODO: Revise these to reflect the forthcoming Identity proposal.
*/
callerIdentity: Identity;
/**
* The connection ID of the caller.
*
* May be `null`, e.g. for scheduled reducers.
*/
callerConnectionId?: ConnectionId;
/**
* The amount of energy consumed by the reducer run, in eV.
* (Not literal eV, but our SpacetimeDB energy unit eV.)
* May be present or undefined at the implementor's discretion;
* future work may determine an interface for module developers
* to request this value be published or hidden.
*/
energyConsumed?: bigint;
/**
* The `Reducer` enum defined by the `moduleBindings`, which encodes which reducer ran and its arguments.
*/
reducer: Reducer;
};Type UpdateStatus
type UpdateStatus =
| { tag: 'Committed'; value: __DatabaseUpdate }
| { tag: 'Failed'; value: string }
| { tag: 'OutOfEnergy' };| Name | Description |
|---|---|
Committed variant | The reducer ran successfully. |
Failed variant | The reducer errored. |
OutOfEnergy variant | The reducer was aborted due to insufficient energy. |
Variant Committed
{
tag: 'Committed';
}
The reducer returned successfully and its changes were committed into
the database state. An Event with tag: 'Reducer'
passed to a row callback must have this status in its
ReducerEvent.
Variant Failed
{
tag: 'Failed';
value: string;
}
The reducer returned an error, panicked, or threw an exception. The
value is the stringified error message. Formatting of the error
message is unstable and subject to change, so clients should use it only
as a human-readable diagnostic, and in particular should not attempt to
parse the message.
Variant OutOfEnergy
{
tag: 'OutOfEnergy';
}
The reducer was aborted due to insufficient energy balance of the module owner.
Type Reducer
type Reducer =
| { name: 'ReducerA'; args: ReducerA }
| { name: 'ReducerB'; args: ReducerB }
The module bindings contains a type Reducer with a variant for each
reducer defined by the module. Each variant has a field args
containing the arguments to the reducer.
Type ReducerEventContext
A ReducerEventContext is a DbContext
augmented with a field event: ReducerEvent.
ReducerEventContexts are passed as the first argument to reducer
callbacks.
| Name | Description |
|---|---|
event field | ReducerEvent containing reducer metadata. |
db field | Provides access to the client cache. |
reducers field | Allows requesting reducers run on the remote database. |
Field event
class ReducerEventContext {
public event: ReducerEvent<Reducer>;
}
The ReducerEvent contained in the
ReducerEventContext has metadata about the reducer which ran.
Field db
class ReducerEventContext {
public db: RemoteTables;
}
The db field of the context provides access to the subscribed view of
the remote database’s tables. See Access the client
cache.
Field reducers
class ReducerEventContext {
public reducers: RemoteReducers;
}
The reducers field of the context provides access to reducers exposed
by the remote module. See Observe and invoke
reducers.
Type SubscriptionEventContext
A SubscriptionEventContext is a DbContext.
Unlike the other context types, SubscriptionEventContext doesn’t have
an event field. SubscriptionEventContexts are passed to subscription
onApplied and
unsubscribeThen callbacks.
| Name | Description |
|---|---|
db field | Provides access to the client cache. |
reducers field | Allows requesting reducers run on the remote database. |
Field db
class SubscriptionEventContext {
public db: RemoteTables;
}
The db field of the context provides access to the subscribed view of
the remote database’s tables. See Access the client
cache.
Field reducers
class SubscriptionEventContext {
public reducers: RemoteReducers;
}
The reducers field of the context provides access to reducers exposed
by the remote module. See Observe and invoke
reducers.
Type ErrorContext
An ErrorContext is a DbContext augmented
with a field event: Error. ErrorContexts are to connections’
onDisconnect and
onConnectError callbacks, and to
subscriptions’ onError callbacks.
| Name | Description |
|---|---|
event field | The error which caused the current error callback. |
db field | Provides access to the client cache. |
reducers field | Allows requesting reducers run on the remote database. |
Field event
class ErrorContext {
public event: Error;
}Field db
class ErrorContext {
public db: RemoteTables;
}
The db field of the context provides access to the subscribed view of
the remote database’s tables. See Access the client
cache.
Field reducers
class ErrorContext {
public reducers: RemoteReducers;
}
The reducers field of the context provides access to reducers exposed
by the remote module. See Observe and invoke
reducers.
Access the client cache
All DbContext implementors, including
DbConnection and
EventContext, have fields .db, which in turn
has methods for accessing tables and views in the client cache.
Each table or view defined by a module has an accessor method, whose
name is the table or view name converted to camelCase, on this .db
field. The accessor methods return table handles. Table handles have
methods for accessing rows and registering
onInsert and onDelete
callbacks. Handles with a known primary key also
expose onUpdate callbacks. Table handles also
offer the ability to find subscribed rows by unique index.
| Name | Description |
|---|---|
| Accessing rows | Iterate over or count subscribed rows. |
onInsert callback | Register a function to run when a row is added to the client cache. |
onDelete callback | Register a function to run when a row is removed from the client cache. |
onUpdate callback | Register a function to run when a subscribed row is replaced with a new version. |
| Unique index access | Seek a subscribed row by the value in its unique or primary key column. |
| BTree index access | Not supported. |
Accessing rows
Method count
class TableHandle {
public count(): number;
}
Returns the number of rows of this table resident in the client cache, i.e. the total number which match any subscribed query.
Method iter
class TableHandle {
public iter(): Iterable<Row>;
}
An iterator over all the subscribed rows in the client cache, i.e. those which match any subscribed query.
The Row type will be an autogenerated type which matches the row type
defined by the module.
Callback onInsert
class TableHandle {
public onInsert(callback: (ctx: EventContext, row: Row) => void): void;
public removeOnInsert(callback: (ctx: EventContext, row: Row) => void): void;
}
The onInsert callback runs whenever a new row is inserted into the
client cache, either when applying a subscription or being notified of a
transaction. The passed EventContext contains an
Event which can identify the change which caused the
insertion, and also allows the callback to interact with the connection,
inspect the client cache and invoke reducers.
The Row type will be an autogenerated type which matches the row type
defined by the module.
removeOnInsert may be used to un-register a previously-registered
onInsert callback.
Callback onDelete
class TableHandle {
public onDelete(callback: (ctx: EventContext, row: Row) => void): void;
public removeOnDelete(callback: (ctx: EventContext, row: Row) => void): void;
}
The onDelete callback runs whenever a previously-resident row is
deleted from the client cache.
The Row type will be an autogenerated type which matches the row type
defined by the module.
removeOnDelete may be used to un-register a previously-registered
onDelete callback.
Callback onUpdate
class TableHandle {
public onUpdate(
callback: (ctx: EventContext, old: Row, new: Row) => void
): void;
public removeOnUpdate(
callback: (ctx: EventContext, old: Row, new: Row) => void
): void;
}
The onUpdate callback runs whenever an already-resident row in the
client cache is updated, i.e. replaced with a new row that has the same
primary key.
Only handles with a declared primary key expose onUpdate callbacks.
Handles for tables or views without a primary key will not have
onUpdate or removeOnUpdate methods. TypeScript view handles expose
onUpdate callbacks when the returned row type has exactly one column
marked with .primaryKey().
The Row type will be an autogenerated type which matches the row type
defined by the module. Its field names are the module’s column names
converted to camelCase, so a column declared trip_id appears as
tripId.
removeOnUpdate may be used to un-register a previously-registered
onUpdate callback.
Unique constraint index access
For each unique constraint on a table, its table handle has a field
whose name is the unique column name. This field is a unique index
handle. The unique index handle has a method
.find(desiredValue: Col) -> Row | undefined, where Col is the type
of the column, and Row the type of rows. If a row with desiredValue
in the unique column is resident in the client cache, .find returns
it.
BTree index access
The SpacetimeDB TypeScript client SDK does not support non-unique BTree indexes.
Observe and invoke reducers
All DbContext implementors, including
DbConnection and
EventContext, have fields .reducers, which in
turn has methods for invoking reducers defined by the module and
registering callbacks on it.
Each reducer defined by the module has three methods on the .reducers:
- An invoke method, whose name is the reducer’s name converted to camel
case, like
setName. This requests that the module run the reducer. It returns aPromise<void>which rejects withSenderErrorif the reducer fails, soawaitit in atry/catchto handle errors. - A callback registation method, whose name is prefixed with
on, likeonSetName. This registers a callback to run whenever we are notified that the reducer ran, including successfully committed runs and runs we requested which failed. This method returns a callback id, which can be passed to the callback remove method. - A callback remove method, whose name is prefixed with
removeOn, likeremoveOnSetName. This cancels a callback previously registered via the callback registration method.
For example:
try {
await conn.reducers.setName({ name: newName });
} catch (err) {
if (err instanceof SenderError) {
console.log(`You made an error: ${err}`);
} else if (err instanceof InternalError) {
console.log(`The server had an error: ${err}`);
}
}React Integration
The SpacetimeDB TypeScript SDK includes React bindings under the
spacetimedb/react subpath. These bindings provide a
SpacetimeDBProvider component and hooks for easily integrating
SpacetimeDB into React applications.
The React integration is fully compatible with React StrictMode and correctly handles the double-mount behavior (only one WebSocket connection is created).
While a SpacetimeDBProvider is mounted, the shared connection manager
also replaces the managed DbConnection if the underlying WebSocket
closes or reports a connection error. Reconnect attempts use exponential
backoff, starting at 1 second and doubling after each consecutive
failure up to a 30 second maximum; the backoff resets after a successful
connection. In browser environments, the manager also re-checks
connection liveness when the page becomes visible, regains focus,
returns online, or is restored from the back-forward cache, so a stalled
reconnect or silently closed socket can be rebuilt promptly after a
suspended tab resumes. Hooks such as useTable observe the provider
state, receive the fresh connection, and establish their subscriptions
again; while the replacement connection is being established, useTable
reports isReady as false until its subscription is applied on the
new connection. This provider-level recovery does not change the
lower-level DbConnection contract: applications that create a
DbConnection directly are still responsible for creating a new
connection if they need reconnection behavior.
| Name | Description |
|---|---|
SpacetimeDBProvider component | Context provider that manages the database connection. |
useSpacetimeDB hook | Access the connection and connection state. |
useTable hook | Subscribe to table data with automatic re-renders. |
useReducer hook | Call reducers from components. |
Component SpacetimeDBProvider
import { SpacetimeDBProvider } from 'spacetimedb/react';
Wrap your application with SpacetimeDBProvider to provide connection
context to child components. Pass a configured DbConnectionBuilder
(without calling .build()).
import { DbConnection, tables } from './module_bindings';
import { SpacetimeDBProvider } from 'spacetimedb/react';
const connectionBuilder = DbConnection.builder()
.withUri('ws://localhost:3000')
.withDatabaseName('my-module')
.onConnect((conn, identity, token) => {
console.log('Connected:', identity.toHexString());
conn.subscriptionBuilder().subscribe(tables.player);
})
.onDisconnect(() => console.log('Disconnected'));
function App() {
return (
<SpacetimeDBProvider connectionBuilder={connectionBuilder}>
<MyComponent />
</SpacetimeDBProvider>
);
}Hook useSpacetimeDB
import { useSpacetimeDB } from 'spacetimedb/react';
function useSpacetimeDB<DbConnection>(): {
isActive: boolean;
identity?: Identity;
token?: string;
connectionId: ConnectionId;
connectionError?: Error;
getConnection(): DbConnection | null;
};
Returns the current connection state and a function to access the connection. The hook re-renders the component when the connection state changes.
function MyComponent() {
const { isActive, identity, getConnection } = useSpacetimeDB<DbConnection>();
const conn = getConnection();
if (!isActive) {
return <div>Connecting...</div>;
}
return (
<div>
<p>Connected as: {identity?.toHexString()}</p>
<button onClick={() => conn?.reducers.createPlayer({ name: 'Alice' })}>
Create Player
</button>
</div>
);
}Hook useTable
import { useTable } from 'spacetimedb/react';
function useTable<TableDef extends UntypedTableDef>(
query: Query<TableDef>,
callbacks?: UseTableCallbacks<RowType<TableDef>>
): [readonly RowType<TableDef>[], boolean];
Subscribe to a table or filtered query and receive automatic re-renders
when rows change. Returns a tuple of [rows, isReady].
import { tables } from './module_bindings';
function PlayerList() {
const [players, isReady] = useTable(tables.player);
if (!isReady) {
return <div>Loading players...</div>;
}
return (
<ul>
{players.map(player => (
<li key={player.id}>{player.name}</li>
))}
</ul>
);
}Hook useReducer
import { useReducer } from 'spacetimedb/react';
import { reducers } from './module_bindings';
const createPlayer = useReducer(reducers.createPlayer);
createPlayer({ name: 'Alice' });
useReducer returns a function that calls the generated reducer. Calls
made before the connection is ready are queued and flushed once the
connection is established.
Identify a client
Type Identity
Identity
A unique public identifier for a client connected to a database.
Type ConnectionId
ConnectionId
An opaque identifier for a client connection to a database, intended to
differentiate between connections from the same
Identity.
Framework Integrations
The SpacetimeDB TypeScript SDK includes built-in integrations for React, SolidJS, Vue, and Svelte. These provide reactive hooks that automatically subscribe to queries and re-render when data changes.
React
import { SpacetimeDBProvider, useSpacetimeDB, useTable, useReducer } from 'spacetimedb/react';SpacetimeDBProvider
Wrap your app in SpacetimeDBProvider to provide the SpacetimeDB
connection to all child components. Pass a configured
DbConnectionBuilder (without calling .build()):
import { SpacetimeDBProvider } from 'spacetimedb/react';
import { DbConnection } from './module_bindings';
const connectionBuilder = DbConnection.builder()
.withUri('wss://maincloud.spacetimedb.com')
.withDatabaseName('my_module')
.onConnect((conn, identity, token) => {
console.log('Connected:', identity.toHexString());
conn.subscriptionBuilder().subscribeToAllTables();
})
.onDisconnect(() => console.log('Disconnected'));
function Root() {
return (
<SpacetimeDBProvider connectionBuilder={connectionBuilder}>
<App />
</SpacetimeDBProvider>
);
}useSpacetimeDB()
Returns the current connection state and a getConnection() function
for accessing the provider-managed DbConnection:
const { isActive, identity, token, getConnection } = useSpacetimeDB<DbConnection>();
const conn = getConnection();useTable(query, callbacks?)
Subscribe to a table or filtered query and return a reactive array of rows:
import { useTable } from 'spacetimedb/react';
import { tables } from './module_bindings';
// Subscribe to all users
const [users, isReady] = useTable(tables.user);
// Subscribe with a filter
const [onlineUsers, isReady] = useTable(
tables.user.where(r => r.online.eq(true))
);
// Subscribe with callbacks
const [onlineUsers, isReady] = useTable(
tables.user.where(r => r.online.eq(true)),
{
onInsert: (user) => console.log('User connected:', user.name),
onDelete: (user) => console.log('User disconnected:', user.name),
onUpdate: (oldUser, newUser) => console.log('User updated:', newUser.name),
}
);
Returns a tuple of [rows, isReady] where rows is a readonly Row[]
and isReady is a boolean indicating whether the subscription has
been applied.
useReducer(reducer)
Returns a function to call a reducer:
import { useReducer } from 'spacetimedb/react';
import { reducers } from './module_bindings';
const sendMessage = useReducer(reducers.sendMessage);
sendMessage({ text: 'Hello!' });SolidJS
import { SpacetimeDBProvider, useSpacetimeDB, useTable, useReducer, useProcedure } from 'spacetimedb/solid';
The SolidJS integration provides primitives that use Solid’s
fine-grained reactivity. useTable takes a getter function so queries
can depend on signals, and returns [readonly Row[], () => boolean]:
import { For, Show, createSignal } from 'solid-js';
import { useTable, useReducer } from 'spacetimedb/solid';
import { reducers, tables } from './module_bindings';
const [onlineOnly, setOnlineOnly] = createSignal(false);
const [users, isReady] = useTable(
() => onlineOnly()
? tables.user.where(r => r.online.eq(true))
: tables.user,
{
onInsert: user => console.log('User connected:', user.name),
onDelete: user => console.log('User disconnected:', user.name),
enabled: () => true,
}
);
const sendMessage = useReducer(reducers.sendMessage);
<Show when={isReady()} fallback={<div>Loading users...</div>}>
<button onClick={() => sendMessage({ text: 'Hello!' })}>Send</button>
<button onClick={() => setOnlineOnly(value => !value)}>Toggle online</button>
<For each={users}>{user => <div>{user.name}</div>}</For>
</Show>
useReducer and useProcedure queue calls made before the connection
is ready and flush them once connected.
Vue
import { SpacetimeDBProvider, useSpacetimeDB, useTable, useReducer } from 'spacetimedb/vue';
The Vue integration provides the same hooks as React. useTable returns
[DeepReadonly<Ref<readonly Row[]>>, DeepReadonly<Ref\<boolean\>>] for
Vue’s reactivity system:
<script setup lang="ts">
import { useTable } from 'spacetimedb/vue';
import { tables } from './module_bindings';
const [users, isReady] = useTable(tables.user);
const [onlineUsers] = useTable(
tables.user.where(r => r.online.eq(true))
);
</script>
<template>
<div v-if="isReady">
<div v-for="user in users" :key="user.identity.toHexString()">
{{ user.name }}
</div>
</div>
</template>Svelte
import { SpacetimeDBProvider, useSpacetimeDB, useTable, useReducer } from 'spacetimedb/svelte';
The Svelte integration provides the same hooks as React. useTable
returns [Readable<readonly Row[]>, Readable\<boolean\>] using Svelte
stores:
<script lang="ts">
import { useTable } from 'spacetimedb/svelte';
import { tables } from './module_bindings';
const [users, isReady] = useTable(tables.user);
const [onlineUsers] = useTable(
tables.user.where(r => r.online.eq(true))
);
</script>
{#if $isReady}
{#each $users as user}
<div>{user.name}</div>
{/each}
{/if}
Last updated Oct 08, 2026