Next.js + PowerSync
A guide for creating a new Next.js application with PowerSync for offline/local first functionality
Introduction
In this tutorial, we’ll explore how to enhance a Next.js application with offline-first capabilities using PowerSync. In the following sections, we’ll walk through the process of integrating PowerSync into a Next.js application, setting up local-first storage, and handling synchronization efficiently.
Setup
Next.js Project Setup
Let’s start by bootstrapping a new Next.js application using create-next-app
.
When running this command you’ll be presented with a few options. The PowerSync suggested selection for the setup options Next.js offers are:
Do not use Turbopack when setting up a new Next.js project as we’ll be updating the next.config.ts
to use Webpack. This is done because we need to enable:
- asyncWebAssembly
- topLevelWait
Install PowerSync Dependencies
Using PowerSync in a Next.js application will require the use of the PowerSync Web SDK and it’s peer dependencies.
In addition to this we’ll also install @powersync/react
, which provides several hooks and providers for easier integration.
js-logger
is very useful for debugging.Next.js Config Setup
In order for PowerSync to work with the Next.js we’ll need to modify the default next.config.ts
to support PowerSync.
Some important notes here, we have to enable asyncWebAssemply
in Webpack, topLevelAwait
is required and for Web Workers, ensure proper file handling.
It’s also important to add web assembly files to static assets for the site. We will not be using SSR because PowerSync does not support it.
Run pnpm dev
to start the development server and check that everything compiles correctly, before moving onto the next section.
Configure a PowerSync Instance
Now that we’ve got our project setup, let’s create a new PowerSync Cloud instance and connect our client to it. For the purposes of this demo, we’ll be using Supabase as the source backend database that PowerSync will connect to.
To set up a new PowerSync instance, follow the steps covered in the Installation - Database Connection docs page.
Configure PowerSync in your project
Add core PowerSync files
Start by adding a new directory in ./src/lib
named powersync
.
AppSchema
Create a new file called AppSchema.ts
in the newly created powersync
directory and add your App Schema to the file. Here is an example of this.
This defines the local SQLite database schema and PowerSync will hydrate the tables once the SDK connects to the PowerSync instance.
BackendConnector
Create a new file called BackendConnector.ts
in the powersync
directory and add the following to the file.
There are two core functions to this file:
fetchCredentials()
- Used to return a JWT token to the PowerSync service for authentication.uploadData()
- Used to upload changes captured in the local SQLite database that need to be sent to the source backend database, in this case Supabase. We’ll get back to this further down.
You’ll notice that we need to add a .env
file to our project which will contain two variables:
NEXT_PUBLIC_POWERSYNC_URL
- This is the PowerSync instance url. You can grab this from the PowerSync Cloud dashboard.NEXT_PUBLIC_POWERSYNC_TOKEN
- For development purposes we’ll be using a development token. To generate one, please follow the steps outlined in Development Token from our installation docs.
Create Providers
Create a new directory in ./src/app/components
named providers
SystemProvider
Add a new file in the newly created providers
directory called SystemProvider.tsx
.
The SystemProvider
will be responsible for initializing the PowerSyncDatabase
. Here we supply a few arguments, such as the AppSchema we defined earlier along with very important properties such as ssrMode: false
.
PowerSync will not work when rendered server side, so we need to explicitly disable SSR.
We also instantiate our BackendConnector
and pass an instance of that to db.connect()
. This will connect to the PowerSync instance, validate the token supplied in the fetchCredentials
function and then start syncing with the PowerSync service.
DynamicSystemProvider.tsx
Add a new file in the newly created providers
directory called DynamicSystemProvider.tsx
.
We can only use PowerSync in client side rendering, so here we’re setting ssr:false
Update layout.tsx
In our main layout.tsx
we’ll update the RootLayout
function to use the DynamicSystemProvider
created in the last step.
Use PowerSync
Reading Data
In our page.tsx
we can now use the useQuery
hook or other PowerSync functions to read data from the SQLite database and render the results in our application.
Writing Data
Using the execute
function we can also write data into our local SQLite database.
Changes made against the local data will be stored in the upload queue and will be processed by the uploadData
in the BackendConnector class.