(updated)
|
min. read

PowerSync Update: July 2025

Kobie Botha

Sync engines keep becoming more popular in the app dev space, and we're thrilled to be part of the journey with you all. 

Here’s what we’ve been up to during July:

Product updates

  • Incremental watched queries: see our feature story below :) 
  • Expo Go support via %%@powersync/adapter-sql-js%%: Expo is a powerful framework for building React Native applications, but until now we weren’t able to support Expo Go because it doesn't support loading our native SQLite extension. To address this, we added support for SQL.js — a pure JavaScript implementation of SQLite. By loading both SQLite and the PowerSync extension in JS, developers can now bypass native builds entirely, enabling a faster and simpler setup when using Expo Go. There are some performance trade-offs when using a JavaScript-based SQLite engine, so this approach is not intended for production use. However, it significantly lowers the barrier to entry for developers getting started with Expo and PowerSync.
  • (Experimental) Raw SQLite tables: One of the benefits of PowerSync being based on a schemaless protocol is that developers don't have to care about migrations: the schema you pass to the SDK is used to create views over JSON data — so things like adding new columns will just work, with data being available immediately. The tradeoff of this convenience is that, since the clients don't use "real" SQLite tables, you can't use column constraints like foreign keys. For some complex queries, the performance of reading data from JSON instead of a structured table can also be limiting. This is why we've recently started experimenting with a feature called raw tables, where developers have full control (and responsibility!) of the SQLite tables to use. If you have use cases where raw tables could help, we'd appreciate your feedback. Specifically, we've heard that defining and migrating raw tables can be hard, and we'd like to look into integrations with packages to help with that. So if you've used raw tables, please reach out to us in our Discord!
  • Email notifications for alerts: Monitoring and alerting was previously something that had to be built using the PowerSync CLI or APIs, and many developers simply weren’t doing this. So we built an alerting and notification system! PowerSync can now email you when an alert on your instance fires or clears — like a database connection error or a replication problem — so that you can catch issues early. If you’re on a paid plan, webhooks are still available to notify external systems too. Learn how to set up alerts and email rules in the Monitoring and Alerting docs.

Inside Syncball

Feature story: Incremental Watched Queries for powersync-js

PowerSync has always provided reactive watched query functionality — perfect for building real-time, reactive UIs with no need for polling or manual update hooks. However, React developers regularly wanted support for React memoization in %%useQuery%%-based components. 

Unfortunately, this wasn’t feasible due to limitations in our original implementation. This led to:

  • Watched queries emitting "updated" results even when the actual data hadn’t changed.
  • Unnecessary component re-renders, even when no UI update was required.

This meant the PowerSync JS SDKs weren't usable out of the box for advanced React apps, and we regularly saw developers building things like in-memory representations of SQLite data on top of PowerSync.

We’ve now addressed these concerns with the release of Incremental Watched Queries.

This is a fundamental upgrade that replaces our original %%PowerSync.watch%% methods, and introduces two new APIs:

  • %%PowerSync.query().watch()%%
  • %%PowerSync.query().differentialWatch()%%

These new APIs enable features like in-memory caching and shared query results. 

To avoid unnecessary emissions and re-renders, developers now provide:

  • A %%comparator%% function for %%query().watch()%%
  • A %%rowComparator%% function for %%query().differentialWatch()%%

These comparators detect meaningful changes between underlying SQLite updates. 

With %%query().watch()%%, false-positive updates are completely eliminated. And with %%query().differentialWatch()%%, we go even further — reporting exactly what rows were added, removed, or updated.

Differential queries also preserve JavaScript array object references for unchanged rows, which makes React memoization effortless to implement.

That’s it for this issue.

Thanks,

The PowerSync Team