Docs
Guide

Guide

Learn how to build a minimal Evolu application. You can use Next.js (opens in a new tab) or any other React (opens in a new tab) meta-framework.

Define Data

To start using Evolu, define schemas for your database and export React Hooks.

import * as Schema from "@effect/schema/Schema";
import * as Evolu from "evolu";
 
const TodoId = Evolu.id("Todo");
type TodoId = Schema.To<typeof TodoId>;
 
const TodoTable = Schema.struct({
  id: TodoId,
  title: Evolu.NonEmptyString1000,
  isCompleted: Evolu.SqliteBoolean,
});
type TodoTable = Schema.To<typeof TodoTable>;
 
const Database = Schema.struct({
  todo: TodoTable,
});
 
export const {
  useQuery,
  useMutation,
  useOwner,
  useOwnerActions,
  useEvoluError,
} = Evolu.create(Database);

Validate Data

Learn more about Schema (opens in a new tab).

import * as Schema from "@effect/schema/Schema";
import * as Evolu from "evolu";
 
Schema.parse(Evolu.String1000)(title);

Mutate Data

Mutation API is designed for local-first apps to ensure changes are always merged without conflicts.

const { create, update } = useMutation();
 
create("todo", { title, isCompleted: false });
update("todo", { id, isCompleted: true });

Query Data

Evolu uses type-safe TypeScript SQL query builder kysely (opens in a new tab), so autocompletion works out-of-the-box.

const { rows } = useQuery(
  (db) => db.selectFrom("todo").select(["id", "title"]).orderBy("updatedAt"),
  // (row) => row
  ({ title, ...rest }) => title && { title, ...rest }
);

Protect Data

Evolu encrypts data with Mnemonic, a safe autogenerated password based on bip39 (opens in a new tab).

const owner = useOwner();
 
alert(owner.mnemonic);

Delete Data

Leave no traces on a device.

const ownerActions = useOwnerActions();
 
if (confirm("Are you sure? It will delete all your local data."))
  ownerActions.reset();

Restore Data

Restore data elsewhere. Encrypted data can only be restored with a Mnemonic.

const ownerActions = useOwnerActions();
 
ownerActions.restore(mnemonic).then((either) => {
  if (either._tag === "Left") alert(JSON.stringify(either.left, null, 2));
});

Handle Errors

Evolu useQuery and useMutation never fail, it's the advantage of local first apps, but Evolu, in rare cases, can.

const evoluError = useEvoluError();
 
useEffect(() => {
  // eslint-disable-next-line no-console
  if (evoluError) console.log(evoluError);
}, [evoluError]);