> ## Documentation Index
> Fetch the complete documentation index at: https://docs.powersync.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Prioritized Sync

> Prioritize which tables sync first so users can start working immediately while remaining data continues loading in the background.

## Overview

PowerSync supports defining sync priorities, which allows you to control the sync order for different data. This is particularly useful when certain data should be available sooner than others.

In Sync Streams, priorities are assigned to streams and PowerSync manages the underlying buckets internally. (In legacy Sync Rules, priorities were assigned to buckets explicitly.)

<Note>
  **Availability**

  This feature was introduced in version **1.7.1** of the PowerSync Service, and in the following SDK versions:

  * [Flutter v1.12.0](/client-sdks/reference/flutter)
  * [React Native v1.18.1](/client-sdks/reference/react-native-and-expo)
  * [JavaScript Web v1.14.2](/client-sdks/reference/javascript-web)
  * [Kotlin v1.0.0-BETA26](/client-sdks/reference/kotlin)
  * [Swift v1.0.0-Beta.8](/client-sdks/reference/swift)
  * [.NET v0.0.6-alpha.1](/client-sdks/reference/dotnet)
</Note>

## Why Use Sync Priorities?

PowerSync's standard sync protocol ensures that:

* The local data view is only updated when a fully consistent checkpoint is available.
* All pending local changes must be uploaded, acknowledged, and synced back before new data is applied.

While this guarantees consistency, it can lead to delays, especially for large datasets or continuous client-side updates. Sync priorities provide a way to speed up syncing of high-priority data while still maintaining overall integrity.

## How It Works

Each bucket is assigned a priority value between 0 and 3, where:

* 0 is the highest priority and has special behavior (detailed below).
* 3 is the default and lowest priority.
* Lower numbers indicate higher priority.

Higher-priority data syncs first, and lower-priority data syncs later. If you only use a single priority, there is no difference between priorities 1-3. The difference only comes in when you use multiple different priorities.

<Tabs>
  <Tab title="Sync Streams">
    In Sync Streams, you assign priorities directly to streams. PowerSync manages buckets internally, so you don't need to think about bucket structure. Each stream with a given priority will have its data synced at that priority level.

    ```yaml theme={null}
    streams:
      lists:
        auto_subscribe: true
        query: SELECT * FROM lists WHERE owner_id = auth.user_id()
        priority: 1  # Syncs first

      todos:
        auto_subscribe: true
        query: SELECT * FROM todos WHERE list_id IN (SELECT id FROM lists WHERE owner_id = auth.user_id())
        priority: 2  # Syncs after lists
    ```

    Clients can also override the priority when subscribing:

    ```js theme={null}
    // Override the stream's default priority for this subscription
    const sub = await db.syncStream('todos', { list_id: 'abc' }).subscribe({ priority: 1 });
    ```

    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.
  </Tab>

  <Tab title="Sync Rules (Legacy)">
    In Sync Rules, you assign priorities to bucket definitions. The priority determines when data in that bucket syncs relative to other buckets.

    ```yaml theme={null}
    bucket_definitions:
      user_lists:
        priority: 1  # Syncs first
        parameters: SELECT id AS list_id FROM lists WHERE user_id = request.user_id()
        data:
          - SELECT * FROM lists WHERE id = bucket.list_id

      user_todos:
        priority: 2  # Syncs after lists
        parameters: SELECT id AS list_id FROM lists WHERE user_id = request.user_id()
        data:
          - SELECT * FROM todos WHERE list_id = bucket.list_id
    ```
  </Tab>
</Tabs>

## Syntax and Configuration

<Tabs>
  <Tab title="Sync Streams">
    In Sync Streams, set the `priority` option on the stream definition:

    ```yaml theme={null}
    streams:
      high_priority_data:
        auto_subscribe: true
        query: SELECT * FROM important_table WHERE user_id = auth.user_id()
        priority: 1

      low_priority_data:
        auto_subscribe: true
        query: SELECT * FROM background_table WHERE user_id = auth.user_id()
        priority: 2
    ```
  </Tab>

  <Tab title="Sync Rules (Legacy)">
    In Sync Rules, priorities can be defined using the `priority` YAML key on bucket definitions, or with the `_priority` attribute inside parameter queries:

    ```yaml theme={null}
    bucket_definitions:
      # Using the `priority` YAML key
      user_data:
        priority: 1
        parameters: SELECT request.user_id() AS id WHERE ...
        data: 
          # ...
      
      # Using the `_priority` attribute (useful for multiple parameter queries with different priorities)
      project_data:
        parameters: SELECT id AS project_id, 2 AS _priority FROM projects WHERE ...
        data: 
          # ...
    ```
  </Tab>
</Tabs>

<Note>
  Priorities must be static and cannot depend on row values within a parameter query.
</Note>

## Example: Syncing Lists Before Todos

Consider a scenario where you want to display lists immediately while loading todos in the background. This approach allows users to view and interact with lists right away without waiting for todos to sync.

<Tabs>
  <Tab title="Sync Streams">
    ```yaml theme={null}
    config:
      edition: 3

    streams:
      lists:
        auto_subscribe: true
        query: SELECT * FROM lists WHERE owner_id = auth.user_id()
        priority: 1  # Syncs first

      todos:
        auto_subscribe: true
        query: |
          SELECT * FROM todos
          WHERE list_id IN (SELECT id FROM lists WHERE owner_id = auth.user_id())
        priority: 2  # Syncs after lists
    ```

    The `lists` stream syncs first (priority 1), allowing users to see and interact with their lists immediately. The `todos` stream syncs afterward (priority 2), loading in the background.
  </Tab>

  <Tab title="Sync Rules (Legacy)">
    ```yaml theme={null}
    bucket_definitions:
      user_lists:
        priority: 1  # Syncs first
        parameters: SELECT id AS list_id FROM lists WHERE user_id = request.user_id()
        data:
          - SELECT * FROM lists WHERE id = bucket.list_id

      user_todos:
        priority: 2  # Syncs after lists
        parameters: SELECT id AS list_id FROM lists WHERE user_id = request.user_id()
        data:
          - SELECT * FROM todos WHERE list_id = bucket.list_id
    ```

    The `user_lists` bucket syncs first (priority 1), allowing users to see and interact with their lists immediately. The `user_todos` bucket syncs afterward (priority 2), loading in the background.
  </Tab>
</Tabs>

## Behavioral Considerations

* **Interruption for Higher Priority Data**: Syncing lower-priority data *may* be interrupted if new data for higher-priority streams/buckets arrives.
* **Local Changes & Consistency**: If local writes fail due to validation or permission issues, they are only reverted after *all* data has synced.
* **Deleted Data**: Deleted data may only be removed after *all* priorities have completed syncing. Future updates may improve this behavior.
* **Data Ordering**: Lower-priority data will never appear before higher-priority data.

## Special Case: Priority 0

Priority 0 buckets sync regardless of pending uploads.

For example, in a collaborative document editing app (e.g., using Yjs), each change is stored as a separate row. Since out-of-order updates don’t affect document integrity, Priority 0 can ensure immediate availability of updates.

Caution: If misused, Priority 0 may cause flickering or inconsistencies, as updates could arrive out of order.

## Consistency Considerations

PowerSync's full consistency guarantees only apply once all priorities have completed syncing.

When higher-priority data is synced, all inserts and updates at that priority level will be consistent. However, deletes are only applied when the full sync completes, so you may still have some stale data at those priority levels.

Consider the following example:

Imagine a task management app where users create lists and todos. Some users have millions of todos. To improve first-load speed:

* Lists are assigned Priority 1, syncing first to allow UI rendering.
* Todos are assigned Priority 2, loading in the background.

Now, if another user adds new todos, it’s possible for the list count (synced at Priority 1) to temporarily not match the actual todos (synced at Priority 2). If real-time accuracy is required, both lists and todos should use the same priority.

## Client-Side Considerations

PowerSync's client SDKs provide APIs to allow applications to track sync status at different priority levels. Developers can leverage these to ensure critical data is available before proceeding with UI updates or background processing. This includes:

1. `waitForFirstSync(priority: int)`. When passing the optional `priority` parameter to this method, it will wait for specific priority level to complete syncing.
2. `SyncStatus.priorityStatusEntries()` A list containing sync information for each priority that was seen by the PowerSync Service.
3. `SyncStatus.statusForPriority(priority: int)` This method takes a fixed priority and returns the sync state for that priority by looking it up in `priorityStatusEntries`.

## Example

Using the above we can render a lists component only once the user's lists (with priority 1) have completed syncing, else display a message indicating that the sync is still in progress:

```dart theme={null}
  // Define the priority level for lists
  static final _listsPriority = BucketPriority(1);

  @override
  Widget build(BuildContext context) {
    // Use FutureBuilder to wait for the first sync of the specified priority to complete
    return FutureBuilder(
      future: db.waitForFirstSync(priority: _listsPriority),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          // Use StreamBuilder to render the lists once the sync completes
          return StreamBuilder(
            stream: TodoList.watchListsWithStats(),
            builder: (context, snapshot) {
              if (snapshot.data case final todoLists?) {
                return ListView(
                  padding: const EdgeInsets.symmetric(vertical: 8.0),
                  children: todoLists.map((list) {
                    return ListItemWidget(list: list);
                  }).toList(),
                );
              } else {
                return const CircularProgressIndicator();
              }
            },
          );
        } else {
          return const Text('Busy with sync...');
        }
      },
    );
  }

```

Example implementations of prioritized sync are also available in the following apps:

* Flutter: [Supabase To-Do List](https://github.com/powersync-ja/powersync.dart/tree/main/demos/supabase-todolist)
* Kotlin:
  * [Supabase To-Do List (KMP)](https://github.com/powersync-ja/powersync-kotlin/blob/main/demos/supabase-todolist/shared/src/commonMain/kotlin/com/powersync/demos/App.kt#L46)
  * [Supabase To-Do List (Android)](https://github.com/powersync-ja/powersync-kotlin/blob/main/demos/android-supabase-todolist/app/src/main/java/com/powersync/androidexample/screens/HomeScreen.kt#L69)
* Swift: [Supabase To-Do List](https://github.com/powersync-ja/powersync-swift/tree/main/Demos/PowerSyncExample)
