Get started with local-first
This guide will get you all set up and ready to use Evolu.
Requirements: TypeScript 5.7 or later with the strict and
exactOptionalPropertyTypes flags enabled in tsconfig.json file.
Installation
Evolu offers SDKs for a variety of frameworks, including React, Svelte, React Native, Expo, and others. Below, you can see how to install the SDKs for each framework.
npm install @evolu/common @evolu/react @evolu/react-web
Define schema
First, define your app database schema—tables, columns, and types.
Evolu uses Type for data modeling. Instead of plain JS types like string or number, we recommend using branded types to enforce domain rules.
import * as Evolu from "@evolu/common";
// Primary keys are branded types, preventing accidental use of IDs across
// different tables (e.g., a TodoId can't be used where a UserId is expected).
const TodoId = Evolu.id("Todo");
type TodoId = typeof TodoId.Type;
// Schema defines database structure with runtime validation.
// Column types validate data on insert/update/upsert.
const Schema = {
todo: {
id: TodoId,
// Branded type ensuring titles are non-empty and ≤100 chars.
title: Evolu.NonEmptyString100,
// SQLite doesn't support the boolean type; it uses 0 and 1 instead.
isCompleted: Evolu.nullOr(Evolu.SqliteBoolean),
},
};
Evolu automatically adds system
columns:
createdAt, updatedAt, isDeleted, and ownerId.
Create Evolu
After defining the schema, create an Evolu instance for your environment.
import { createEvolu, SimpleName } from "@evolu/common";
import { createUseEvolu, EvoluProvider } from "@evolu/react";
import { evoluReactWebDeps } from "@evolu/react-web";
const evolu = createEvolu(evoluReactWebDeps)(Schema, {
name: SimpleName.orThrow("your-app-name"),
transports: [{ type: "WebSocket", url: "wss://your-sync-url" }], // optional, defaults to free.evoluhq.com
});
// Wrap your app with <EvoluProvider>
<EvoluProvider value={evolu}>
{/* ... */}
</EvoluProvider>
// Create a typed React Hook returning an instance of Evolu
const useEvolu = createUseEvolu(evolu);
// Use the Hook in your app
const { insert, update } = useEvolu();
Mutate data
const { insert, update } = useEvolu();
const result = insert("todo", {
title: "New Todo",
isCompleted: Evolu.sqliteFalse,
});
if (result.ok) {
update("todo", { id: result.value.id, isCompleted: Evolu.sqliteTrue });
}
Query data
Evolu uses type-safe TypeScript SQL query builder Kysely, so autocompletion works out-of-the-box.
Let's start with a simple Query.
const allTodos = evolu.createQuery((db) => db.selectFrom("todo").selectAll());
Once we have a query, we can load or subscribe to it.
import { useQuery } from "@evolu/react";
// ...
const todos = useQuery(allTodos);
Delete data
To delete a row, set isDeleted to sqliteTrue (1). Evolu uses soft deletes instead of permanent deletion.
const { update } = useEvolu();
// Mark a todo as deleted
update("todo", { id: todoId, isDeleted: Evolu.sqliteTrue });
When querying, filter out deleted rows:
const activeTodos = evolu.createQuery((db) =>
db
.selectFrom("todo")
.selectAll()
// Filter out deleted rows
.where("isDeleted", "is not", Evolu.sqliteTrue)
.orderBy("createdAt"),
);
Evolu does not permanently delete data—it marks them as deleted to support merging across devices and time travel. This is essential for local-first systems where devices sync asynchronously. See Time Travel to learn how to recover deleted data.
Protect data
Privacy is essential for Evolu, so all data are encrypted with an encryption key derived from a cryptographically strong secret (which can be represented as a mnemonic) or provided by an external hardware device.
import { use } from "react";
// ...
const evolu = useEvolu();
const owner = use(evolu.appOwner);
console.log(owner.mnemonic);
// this will print the mnemonic in the console
Purge data
To clear all local data from the device (this is different from soft deletes):
evolu.resetAppOwner();
This removes all data from the local database. This is not a soft delete—it's a complete reset.
Restore data
To restore synced data on any device:
evolu.restoreAppOwner(mnemonic);
To learn more about Evolu, explore our playgrounds and examples.