
Learn how to build lightning-fast offline-first mobile apps using WatermelonDB and SQLite for local data persistence and privacy.
When I built Nept Track, an offline habit tracker, budget planner, and notes app, my top priority was instant response times and total user privacy. Users expect mobile apps to work seamlessly whether they are sitting on a fast fiber connection or deep underground in a subway tunnel without any signal. If an app shows a loading spinner just to save a simple note or mark a habit as complete, it feels sluggish and outdated.
Building apps that prioritize offline functionality from day one changes how you architecture your software. Instead of treating local storage as a temporary cache, you treat the device database as the primary source of truth. In this guide, I want to share how I use WatermelonDB powered by SQLite to build lightning-fast, privacy-friendly mobile applications.
When starting out with mobile development, it is tempting to use simple key-value stores like AsyncStorage in React Native for everything. While it works fine for storing a user auth token or a dark mode toggle, it completely falls apart once your app handles relational data.
Imagine building a budget tracker or a notes app. You have users, categories, transactions, and tags linked together with foreign keys. Trying to query, filter, and sort complex relationships stored inside a massive JSON string in a key-value store will quickly freeze the JavaScript thread.
To build robust offline-first experiences, you need a true relational database that runs directly on the device. That is where SQLite comes in, but writing raw SQL queries inside a modern reactive framework can get messy fast. That is why I rely on WatermelonDB.
WatermelonDB is a high-performance reactive database framework built for React Native and web apps. It is specifically designed to scale to tens of thousands of records without slowing down your UI.
The secret behind WatermelonDB's speed is laziness. It does not load all your data into memory at once. Instead, it uses lazy loading and observable queries. When a piece of data changes in the database, only the specific UI components subscribed to that query re-render. Everything else stays untouched, resulting in buttery-smooth 60fps animations.
Tagged with
Leave a comment
Let us look at how to set up a basic schema for an offline-first app. First, you define your database schema using WatermelonDB definitions.
import { appSchema, tableSchema } from '@nozbe/watermelondb'
export default appSchema({
version: 1,
tables: [
tableSchema({
name: 'habits',
columns: [
{ name: 'title', type: 'string' },
{ name: 'streak', type: 'number' },
{ name: 'is_completed', type: 'boolean' },
{ name: 'created_at', type: 'number' },
]
}),
]
})
Next, you define your Models. Models map your database tables to JavaScript classes, making it easy to add custom methods and business logic right next to your data.
import { Model } from '@nozbe/watermelondb'
import { field, text, date, action } from '@nozbe/watermelondb/decorators'
export default class Habit extends Model {
static table = 'habits'
@text('title') title
@field('streak') streak
@field('is_completed') isCompleted
@date('created_at') createdAt
@action async toggleComplete() {
await this.update(habit => {
habit.isCompleted = !habit.isCompleted
if (habit.isCompleted) {
habit.streak += 1
}
})
}
}
One of the best side effects of building offline-first apps is privacy. When your application stores data locally on the user's device using encrypted SQLite storage, you minimize the need to send sensitive personal data to a remote cloud server.
In apps like Nept Track, users manage personal budgets and daily habits. By keeping this data local by default, the user retains total ownership. There are no analytics trackers reading their personal notes, and no risk of a cloud database leak exposing their private financial logs. If you do need to sync data across multiple devices for a user, you can implement an end-to-end encrypted sync mechanism where the server only sees encrypted blobs it cannot decrypt.
Inevitably, offline-first apps need to sync with a backend server when an internet connection becomes available. WatermelonDB provides a built-in sync engine that makes this straightforward.
The sync protocol works on a pull-changes and push-changes strategy:
Keeping your sync logic decoupled from your UI ensures that your app remains usable even when network requests fail or timeout.
Building offline-first apps takes a bit more upfront architectural thought than simply connecting to a REST API, but the payoff is massive. Your users get an app that opens instantly, works everywhere, and respects their privacy.
If you are planning a new mobile app or SaaS product and want to build it right from the ground up, get in touch and let us discuss your project.