Evolu - React Hooks for Local-First Apps
React Hooks library for local-first apps (opens in a new tab) with end-to-end encrypted backup and sync using SQLite (opens in a new tab) and CRDT (opens in a new tab).
Evolu is designed for privacy, ease of use, and no vendor lock-in.
Local-first apps allow users to own their data. Evolu stores data in the user's device(s), so Evolu apps can work offline and without a specific server. Evolu Server
Evolu merges data changes made on different devices without conflicts using Conflict-free Replicated Data Type (CRDT). How Evolu Works
Getting Started · GitHub Repository (opens in a new tab) · Twitter (opens in a new tab)
Overview
import { pipe } from "@effect/data/Function";
import * as Schema from "@effect/schema/Schema";
import * as Evolu from "evolu";
const TodoId = Evolu.id("Todo");
type TodoId = Schema.To<typeof TodoId>;
const NonEmptyString50 = pipe(
Schema.string,
Schema.minLength(1),
Schema.maxLength(50),
Schema.brand("NonEmptyString50")
);
type NonEmptyString50 = Schema.To<typeof NonEmptyString50>;
const TodoTable = Schena.struct({
id: TodoId,
title: NonEmptyString50,
description: Schema.nullable(Evolu.NonEmptyString1000),
isCompleted: Evolu.SqliteBoolean,
});
type TodoTable = Schema.To<typeof TodoTable>;
const Database = Schema.struct({
todo: TodoTable,
});
const { useQuery, useMutation } = Evolu.create(Database);
const { rows } = useQuery(
(db) =>
// A type-safe SQL query builder.
db.selectFrom("todo").select(["id", "title"]).orderBy("createdAt"),
// The filterMap helper for ad-hoc schema migrations.
(row) => row
);
const { create, update } = useMutation();
if (!Schema.is(NonEmptyString50)(title)) return;
const { id } = create("todo", { title, isCompleted: false });
update("todo", { id, isCompleted: true }, onComplete);
Features
- The official SQLite Wasm in all browsers (React Native and Electron soon)
- E2E encrypted sync and backup with CRDT (merging changes without conflicts)
- Free Evolu server for testing (paid production-ready soon, or you can run your own)
- Typed database schema with branded types (
NonEmptyString1000
,PositiveInt
) - Reactive queries with React Suspense support
- Real-time experience via revalidation on focus and network recovery
- Schema evolving via
filterMap
ad-hoc migration - No signup/login, no email collection, only Bitcoin-like mnemonic (12 words)
Community
The Evolu community is on GitHub Discussions (opens in a new tab). To chat with other community members, you can join the Evolu Discord (opens in a new tab).