Skip to main content

My First Replicache Feature

Let's see how easy it is to add a full-stack feature using Replicache. We will add an "urgent" flag to our Todos, and the ability to toggle and persist this property.

Modify the Model

First let's add the urgent boolean to the Todo model.


Because there is already data stored on the client and server that doesn't have this field, we can't mark it required. In a real application you could also use the schemaVersion feature to migrate the old data, which would allow you to make the new field required.

import {ReadTransaction} from 'replicache';

export type Todo = {
id: string;
text: string;
completed: boolean;
sort: number;

// Add this property.
urgent?: boolean;

export type TodoUpdate = Partial<Todo> & Pick<Todo, 'id'>;

export async function listTodos(tx: ReadTransaction) {
return (await tx.scan().values().toArray()) as Todo[];

Add a Mutator

We need a mutator to save the new field.

The example app already has an updateTodo mutator that handles all fields of Todo, so there's nothing to do here 🎉:

export const mutators = {
// This already stores all fields of `Todo`. Whee.
updateTodo: async (tx: WriteTransaction, update: TodoUpdate) => {
const prev = (await tx.get( as Todo;
const next = { ...prev, ...update };
await tx.put(, next);

It's common for Replicache apps to have basic CRUD-style mutators for each entity they support. But it's also possible to create more complex mutators for more interesting situations. See the createTodo mutator in mutators.ts for an example.

Add a Toggle Button

We need to add a UI element so that the user can toggle the "urgent" flag. This is simple to do since the mutator we need is already available in this component as onUpdate.

<div className="view">
{/* add this button to the view div right before the "destroy" button */}
position: 'absolute',
top: 0,
bottom: 0,
right: 50,
width: 40,
height: 40,
fontSize: 30,
margin: 'auto 0',
onClick={() => onUpdate({id, urgent: !todo.urgent})}

<button className="destroy" onClick={() => onDelete()} />

At this point, we have actually finished the basic plumbing of our feature. Clicking on this button will: 1) change the state of our app (immediately), 2) persist that change, and 3) cause that change to be synchronized in real time to other browsers.

Show the "urgent" flag in the UI

Just to prove to ourselves that this is happening, let's change the look of the todo when it's urgent:

{/* add the style attribute to change the todo-item's background when urgent */}
backgroundColor: todo.urgent ? "red" : "",

It's not beautiful, but you get the idea. In summary, developers can often implement a feature by writing relatively simple code in one place. The data changes associated with that feature will automatically be full-stack and synchronized to other instances of the app.


Now let's Deploy our app to production.