Skip to main content
After defining your streams on the server-side, your client app subscribes to them to start syncing data (this is an explicit operation unless streams are configured to auto-subscribe). This page covers everything you need to use Sync Streams from your client code.

Quick Start

Streams that are configured to auto-subscribe will automatically start syncing as soon as you connect to your PowerSync instance in your client-side application. For any other streams, the basic pattern is: subscribe to a stream, wait for data to sync, then unsubscribe when done.
Tauri SDK: The JavaScript API shown in the TypeScript/JavaScript tabs throughout this page applies to Tauri as well. Import from @powersync/tauri-plugin (for PowerSyncTauriDatabase) or @powersync/common (for shared types). See the Connection Parameters section below for how Tauri handles connect-time parameters differently.

Framework Integrations

Most developers use framework-specific hooks that handle subscription lifecycle automatically.
The PowerSync React package provides three hooks relevant to Sync Stream subscriptions. Each serves a distinct role:
  • useQuery: queries the local SQLite database and returns live-updating results. It also accepts a streams option to subscribe to streams alongside the query — use this when the stream is only needed for that specific query.
  • useSyncStream: subscribes to a single named Sync Stream. Use this when the same stream’s data is needed across multiple queries or components; the subscription lives independently of any particular useQuery call and is canceled when the component unmounts.
  • useSyncStreams: the same as useSyncStream but for a variable number of streams. Accepts an array of stream options; all subscriptions are canceled when the component unmounts or the array changes.

useQuery with streams: Tethering a Subscription to a Query

useQuery accepts an optional streams option that subscribes to one or more streams and ties their lifecycle to that specific query. This is the simplest option when the stream is only ever needed alongside this one query:

useSyncStream: Single Stream

Use this when a component needs data from exactly one stream.

useSyncStreams: Variable Number of Streams

Use this when the number of streams is only known at runtime — for example, when it depends on an array from props or state:

Type-Safe Stream Wrappers

When you generate your client-side schema from the PowerSync Dashboard or CLI, typed stream wrappers are generated alongside the schema for all SDKs. These catch typos in stream names and parameter names at compile time — mistakes that would otherwise cause silent data-missing bugs only detectable by inspecting sync status. For example, without typed wrappers this fails silently — no error, but data won’t sync:

The Generated Code

The schema generator produces typed wrappers at the bottom of your generated schema file. Given streams like:
The generated output (JavaScript/TypeScript) looks like:

Usage

Use the generated wrappers instead of calling db.syncStream() directly. Each method returns a SyncStream, so you can chain .subscribe(), .subscribe({ ttl, priority }), and all other methods covered on this page.
Type-safe wrappers are only generated for streams that do not have auto_subscribe: true. Auto-subscribe streams start syncing automatically on connect and don’t require explicit client subscriptions, so no wrapper is generated for them.

Checking Sync Status

You can check whether a subscription has synced and monitor download progress:

TTL (Time-To-Live)

TTL controls how long data remains cached after you unsubscribe. This enables “warm cache” behavior — when users navigate back to a screen, data may already be available without waiting for a sync. Default behavior: Data is cached for 24 hours after unsubscribing. For most apps, this default works well.

Setting a Custom TTL

How TTL Works

  • Per-subscription: Each (stream name, parameters) pair has its own TTL.
  • First subscription wins: If you subscribe to the same stream with the same parameters multiple times, the TTL from the first subscription is used.
  • After unsubscribe: Data continues syncing for the TTL duration, then is removed from the client-side SQLite database.

Priority Override

Streams can have a default priority set in the YAML sync configuration (see Prioritized Sync). When subscribing, you can override this priority for a specific subscription:
When different components subscribe to the same stream with the same parameters but different priorities, PowerSync uses the highest priority for syncing. That higher priority is kept until the subscription ends (or its TTL expires). Subscriptions with different parameters are independent and do not conflict.

Connection Parameters

Connection parameters are a more advanced feature for values that apply to all streams in a session. They’re the Sync Streams equivalent of Client Parameters in legacy Sync Rules.
For most use cases, subscription parameters (passed when subscribing) are more flexible and recommended. Use connection parameters only when you need a single global value across all streams, like an environment flag.
Define streams that use connection parameters:
Set connection parameters when connecting:
Tauri SDK: Since connect() must be called from Rust for Tauri, connection parameters are passed via SyncOptions in your Rust connector. See the Tauri SDK reference for details on setting up the Rust connector.

Opting Out of Auto-Subscribed Streams

By default, every stream defined with auto_subscribe: true starts syncing as soon as the client connects. For advanced use cases where the client needs more control over what syncs at connect time, you can set includeDefaultStreams: false on the connect options. The auto_subscribe: true flag is then ignored for that connection, and those streams only sync if the client explicitly calls db.syncStream(name, params).subscribe(). Most apps will not need this option. Defining streams without auto_subscribe: true and subscribing on-demand from the client generally achieves a similar result with less indirection. One scenario where it can help is during a migration from auto-subscribed streams to on-demand subscriptions. You keep the auto-subscribed streams in place so older client versions continue to sync correctly, while newer versions that explicitly subscribe to the streams they need set includeDefaultStreams: false to skip those defaults.

API Reference

For quick reference, here are the key methods available in each SDK: