Skip to content

powersync-community/react-native-network-status

Repository files navigation

PowerSync Network Status Demo

A React Native (Expo) demo app built with PowerSync and Supabase that explores three different approaches to signaling PowerSync network quality to end users. PowerSync handles sync between the local SQLite database and Supabase (Postgres), while Supabase provides the backend database and anonymous authentication.

Each hook takes a different angle, and each has known shortcomings. Pick the one that best matches what you want to communicate to users.

Calibrate the thresholds for your app. Every app, dataset, and user network profile is different. The constants exported by each hook (PING_RTT_THRESHOLDS, OPS_PER_SECOND_THRESHOLDS, and the durations passed to useDownloadSpeed) are arbitrary defaults chosen for this demo. Measure real sessions on real devices with your own Sync Config and data shapes, then tune the boundaries until "good / moderate / poor" actually line up with the experience your users are having. Do not ship these numbers as-is.

The three hooks

1. useLivePing - latency to the PowerSync instance

src/hooks/useLivePing.ts issues a one-shot GET to the PowerSync URL and measures the round-trip time.

export const PING_RTT_THRESHOLDS = {
  good: 150,     // <= 150 ms -> good
  moderate: 500  // <= 500 ms -> moderate, else poor
};
Quality Condition
Good RTT <= 150 ms
Moderate RTT <= 500 ms
Poor RTT > 500 ms
Offline PowerSync disconnected or fetch failed
Unknown Button not pressed yet

Shortcomings:

  • Latency, not bandwidth. A fast ping does not mean sync will be fast. A user on a high-latency satellite link with plenty of bandwidth will look "poor" here even though large syncs may still complete in reasonable time, and a user on a low-latency mobile link with saturated upstream may look "good" while sync stalls.
  • Single sample. One request can be skewed by a cold TCP/TLS handshake, a transient radio wakeup, or background contention on the device.
  • Not representative of the sync stream. The ping hits the HTTP endpoint but does not open the /sync/stream channel, so it cannot detect issues that only manifest under a long-lived streaming connection.

2. useDownloadSpeed - measured throughput from /sync/stream

src/hooks/useDownloadSpeed.ts POSTs to the same /sync/stream endpoint the SDK uses, measures bytes received via XMLHttpRequest's onprogress, and reports Mbps / KB/s.

The UI exposes a compressed vs uncompressed switch:

  • Compressed (Accept-Encoding: gzip, br) - measures the decompressed payload size the SDK sees. Reflects perceived sync UX, but inflates numbers vs the actual wire speed because PowerSync NDJSON payloads compress roughly 3-5x.
  • Uncompressed (Accept-Encoding: identity) - forces the server to send raw bytes. Closer to true network bandwidth.

Shortcomings:

  • Duration bias. The longer the test runs, the higher the reported throughput tends to climb. TCP slow-start, TLS warmup, and server-side buffering all depress the early numbers. A 2-second test will almost always report a lower number than a 20-second test on the same connection.
  • Compression ambiguity. The compressed number is not your network speed, and the uncompressed number is not the throughput the SDK actually experiences. Users need both to understand what they are seeing.
  • Shares the network with the active sync. If a real PowerSync sync is running concurrently, the speed test competes with it for bandwidth and under-reports.
  • One-shot, button-driven. No historical averaging, no smoothing. A single bad sample can mislead.

3. useNetworkStatus - operations per second during active sync

src/hooks/useNetworkStatus.ts watches PowerSync's downloadProgress.downloadedOperations while dataFlowStatus.downloading is true, and classifies throughput in ops/sec.

export const OPS_PER_SECOND_THRESHOLDS = {
  good: 10000,    // >= 10,000 ops/sec -> good
  moderate: 5000, // >= 5,000 ops/sec -> moderate
  // anything below -> poor
};
Quality Condition
Good >= 10,000 ops/sec
Moderate >= 5,000 ops/sec
Poor < 5,000 ops/sec
Offline PowerSync disconnected
Unknown No download measured yet

After a download finishes, the last measured ops/sec stays visible.

Shortcomings:

  • Duration bias, same as the speed test. The longer a download runs, the higher ops/sec tends to climb as the connection warms up. A quick 500-op delta looks slower than a sustained 100k-op sync on the same network.
  • Operations are not equal-sized. Ops/sec is a count, not a data-rate. One operation might be a 50-byte row insert, the next a multi-kilobyte document update. A user syncing many tiny rows will appear "good" while a user syncing fewer but larger operations on the same network will appear "poor".
  • Poor fit for incremental sync. The thresholds are calibrated for bulk initial-sync scenarios. Once the user is caught up, normal incremental traffic delivers a handful of ops at a time and the classification will swing to "poor" or "unknown" despite the network being perfectly healthy.
  • Only works during active download. When PowerSync is idle there is nothing to measure, so the hook goes quiet.

Which one should I use?

  • Want to show users "is PowerSync reachable and responsive right now" as a quick health check? useLivePing.
  • Want to show users "how fast is my network for PowerSync traffic specifically" on demand? useDownloadSpeed (be clear in the UI whether you are showing compressed or uncompressed).
  • Want to show users live feedback during a large initial sync? useNetworkStatus, knowing it is only meaningful while downloading and only for workloads whose ops are roughly uniform in size.

A production app would likely combine all three, gated by which one has fresh data at any given moment.

Features

  • Three independent network-quality signals with classification and live UI
  • Seed/delete buttons to generate large datasets (20k customers) for testing sync performance
  • Download progress bar showing real-time sync progress (X of Y operations)
  • Anonymous auth via Supabase, no sign-in required

Prerequisites

  • Node.js 18+
  • pnpm
  • Xcode (for iOS) or Android Studio (for Android)
  • A Supabase project
  • A PowerSync instance connected to your Supabase project

Supabase setup

  1. Enable anonymous sign-in in your Supabase dashboard:

    • Go to Authentication > Providers > Anonymous Sign-In and enable it.
  2. Create a customers table. This demo uses a serial primary key; PowerSync clients always see id as text, so the Sync Config below casts it:

create table public.customers (
  id serial not null,
  name text not null,
  created_at text not null,
  constraint customers_pkey primary key (id)
) tablespace pg_default;

The insert/update/delete policies above are wide open because this is a demo. In a real app, scope them to the authenticated user (for example auth.uid() = owner_id).

  1. Create the powersync publication. PowerSync replicates from Postgres via logical replication and reads the tables listed in a publication named powersync. On Supabase, wal_level = logical and the replication role are already set up for you, so you only need to create the publication:
create publication powersync for table public.customers;
  1. Configure the PowerSync Sync Config (edition 3 / sync streams). In the PowerSync dashboard, set the Sync Config for your instance to:
config:
  edition: 3

streams:
  customers:
    auto_subscribe: true
    query: SELECT * FROM customers

Getting started

  1. Install dependencies

    pnpm install
  2. Configure environment variables

    cp .env.template .env

    Edit .env:

    EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
    EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
    EXPO_PUBLIC_POWERSYNC_URL=https://your-instance.powersync.journeyapps.com
    
  3. Generate native projects

    pnpm prebuild
  4. Run the app

    pnpm ios
    # or
    pnpm android

Tech stack

  • Expo SDK 54 (bare workflow via prebuild)
  • PowerSync (@powersync/react-native + @powersync/op-sqlite)
  • Supabase (@supabase/supabase-js)
  • op-sqlite (@op-engineering/op-sqlite) for the SQLite driver

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors