DATE:
AUTHOR:
PowerSync Product Team
PowerSync Service

Sync Streams Are Now in Beta

DATE:
AUTHOR: PowerSync Product Team

Overview

Sync Streams are in Beta and are now the recommended way to sync data with PowerSync.

The original Sync Rules system was designed to sync data upfront when a client connects — which works well for offline-first apps but creates friction for web apps and other use-cases where you want to sync only the data needed for the current view. Sync Streams address this directly: you define streams of data once (on the server, as with Sync Rules), and your app subscribes to the streams it needs, when it needs them. Multiple tabs, each with their own subscriptions and their own parameters, work independently with no extra state management. Built-in TTL keeps data cached after unsubscribing, so returning to a view doesn't require a loading state.

This Beta release also ships several new query capabilities that were missing in Early Alpha — JOINs, CTEs, multiple queries per stream, nested subqueries, and table-valued functions — making it practical to express complex data access patterns cleanly.

Beta: Sync Streams are in Beta while we collect more feedback. In our terminology, that means they are production-ready and we recommend using Sync Streams for new projects. Sync Rules remain fully supported but are now considered legacy. Sync Rules will be deprecated eventually, and we will communicate a minimally disruptive LTS plan to existing users to help them migrate.

What's new since Early Alpha

  • JOINs: Express complex relationships with INNER JOIN/ JOIN syntax. 

config:
  edition: 3 # All new query features require edition: 3 in your sync config

streams:
  my_issues:
    query: |
      SELECT issues.* FROM issues
        INNER JOIN projects ON projects.id = issues.project_id
        INNER JOIN memberships ON memberships.project_id = projects.id
      WHERE memberships.user_id = auth.user_id()
  • Common Table Expressions (CTEs): Define reusable subqueries once and reference them across multiple queries in a stream, avoiding duplication for patterns like "organizations this user belongs to."

streams:
  project_data:
    with:
      my_orgs: |
        SELECT * FROM organisations
        WHERE id IN (SELECT org_id FROM memberships WHERE user_id = auth.user_id())
    queries:
      - SELECT * FROM my_orgs
      - SELECT * FROM projects WHERE org_id IN (SELECT id FROM my_orgs)
      - SELECT * FROM team_members WHERE org_id IN (SELECT id FROM my_orgs)
  • Multiple queries per stream: Combine related queries into a single stream so they share a subscription. Previously, each table required a separate stream.

streams:
  user_data:
    queries:
      - SELECT * FROM settings WHERE user_id = auth.user_id()
      - SELECT * FROM notes WHERE user_id = auth.user_id()
      - SELECT * FROM preferences WHERE user_id = auth.user_id()
  • Nested subqueries: Query across multiple levels of relationships without denormalizing your schema.

streams:
  my_issues:
    query: |
      SELECT * FROM issues WHERE board_id IN (
        SELECT issue_board FROM projects WHERE id IN (
          SELECT project_id FROM shared_projects WHERE shared_with = auth.user_id()
        )
      )
  • Table-valued functions: Expand array parameters from JWT claims or subscription parameters, reaching feature parity with Sync Rules for this pattern.

streams:
  project_instances:
    query: |
      SELECT i.* FROM instances i
        INNER JOIN json_each(auth.parameter('project_ids')) AS p
        WHERE i.project_id = p.value

These query features are backed by a new compiler that also ships several internal improvements with direct impact:

  • Parameter deduplication: When a query references the same value multiple ways (e.g. as both a subscription parameter and inside a subquery), the compiler detects the overlap and merges the lookups.

  • Query pattern reuse: Identical subquery patterns shared across multiple streams - especially common when using CTEs - now reuse the same internal index rather than creating duplicates, reducing overhead for complex permission models.

  • Improved validation: Clearer error messages when sync config is invalid, making it easier to identify and fix issues before deploying.

Finally, Sync Stream support is now also available in our TanStack React Query package — useQuery and useQueries accept a streams option — and in the Vue and Nuxt packages via the useSyncStream composable, in addition to the existing React Hooks support. Example usage is documented in the Sync Streams client usage docs.

Getting started

See the Sync Streams overview for quickstart examples, client-side subscription API for all SDKs, and a full reference of stream definition options.

config:
  edition: 3

streams:
  my_todos:
    auto_subscribe: true
    query: SELECT * FROM todos WHERE owner_id = auth.user_id()

Requirements

  • PowerSync Service v1.20.0+ (PowerSync Cloud instances already meet this)

  • Latest SDK versions with the Rust-based sync client (enabled by default in current SDK releases)

  • config: edition: 3 in your sync config

See the minimum SDK version table for details, and instructions for enabling the Rust client on older SDK versions if needed.

Migrating from Sync Rules

Existing Sync Rules configurations continue to work — migration is optional but encouraged. When you're ready:

  1. Generate a Sync Streams draft: The PowerSync Dashboard now includes a Migrate to Sync Streams button that automatically converts your existing Sync Rules into a Sync Streams draft for review before deploying. The output uses auto_subscribe: true by default, preserving your existing sync-everything-upfront behavior with no client-side changes required.

  2. Review the draft and deploy.

  3. Optionally, migrate individual streams to on-demand subscriptions over time: removing auto_subscribe: true and updating client code to use the syncStream() API where it makes sense for your app.

See the Migration Guide for syntax changes, before/after examples, and client-side update instructions.

Resources

Feedback and help

Share feedback in our Discord or on GitHub. If you hit issues migrating from Sync Rules or run into query patterns that don't work as expected, please let us know. Feedback from migrations is especially useful at this stage.

Powered by LaunchNotes