Transactions & Mutations
Every change to the data model — creating a document, editing a cell, reordering columns, adding a filter — goes through the transaction system. This is not optional. Direct entity mutation is not supported.
Why: transactions enable undo/redo, debounced persistence to your data source, and automatic rollback if persistence fails.
Basic Operations
Section titled “Basic Operations”Access the transaction from CollectionManager:
const manager = state.manager;const transaction = manager.transaction;Update an Entity
Section titled “Update an Entity”// Update a document's property valueconst document = manager.collection.documents.get("doc-1");const propertyValue = document.getProp("status");
transaction.update(propertyValue, { value: "done" });
// Update a view's columnsconst view = manager.views[0];transaction.update(view, { columns: [ { propertyId: "name", width: 300, visible: true }, { propertyId: "status", width: 150, visible: true }, ],});
// Update collection metadatatransaction.update(manager.collection, { name: "My Tasks" });Create an Entity
Section titled “Create an Entity”// Create a new property (column) on the collectiontransaction.create("propertyId", manager.collection, "new-prop-id", { label: "Due Date", type: "date",});
// Create a new document -- prefer the convenience methodmanager.createDocument();
// Or with optionsmanager.createDocument({ viewId: view.id, // which view to add the row to id: "custom-id", // optional custom document ID meta: { source: "import" }, // optional metadata});Delete an Entity
Section titled “Delete an Entity”// Delete a documentconst document = manager.collection.documents.get("doc-1");transaction.delete(document);
// Delete a property (column)const property = manager.collection.schema["priority"];transaction.delete(property);How Persistence Works
Section titled “How Persistence Works”The TransactionManager handles persistence automatically:
- You call
transaction.update(),transaction.create(), ortransaction.delete() - The change is applied to the in-memory model immediately (optimistic)
- Operations accumulate in a buffer
- After a debounce period (default 250ms), accumulated operations are grouped by entity type and persisted to your data sources in parallel
- If persistence fails, the
TransactionManagerautomatically rolls back by applying inverse operations in reverse order
You don’t need to think about this. It just works. But if you need to force-flush pending operations:
await manager.flush();Configuring Persistence
Section titled “Configuring Persistence”Set the debounce period during initialization:
const state = useCollectionManager({ collectionId: "my-collection", user: "user-1", initialize: async () => ({ collectionSource, documentSource, persistDebounceMs: 500, // default: 250 }),});Undo/Redo
Section titled “Undo/Redo”The UndoRedoManager groups operations into batches with a 500ms window. Rapid edits within 500ms are grouped into a single undo unit.
Using the Hook
Section titled “Using the Hook”The simplest way to enable undo/redo:
import { useUndoRedoHotkeys } from "@blocknote/block-view/react";
function MyTable({ table }) { // Registers Ctrl+Z (undo) and Ctrl+Shift+Z (redo) useUndoRedoHotkeys(table.undoRedoManager);
return <TableView table={table} editable />;}Programmatic Undo/Redo
Section titled “Programmatic Undo/Redo”const undoRedo = manager.undoRedo;
undoRedo.canUndo(); // booleanundoRedo.canRedo(); // booleanundoRedo.undo(); // undo the last batchundoRedo.redo(); // redo the last undone batchHow Batching Works
Section titled “How Batching Works”Operations within 500ms are grouped:
// These three updates become ONE undo unittransaction.update(prop1, { value: "a" });transaction.update(prop2, { value: "b" });transaction.update(prop3, { value: "c" });// User presses Ctrl+Z → all three are undone at once
// Wait 600ms, then:transaction.update(prop4, { value: "d" });// This is a separate undo unitAutomatic Rollback
Section titled “Automatic Rollback”If a persistence call fails (network error, server error), the TransactionManager automatically:
- Applies the inverse of each failed operation in reverse order
- Restores the model to its pre-mutation state
- Emits a
mutationsRolledBackevent
Listen for rollbacks:
manager.addHooks({ mutationsRolledBack: ({ entries }) => { console.error("Changes rolled back:", entries.length, "operations"); // Show a toast notification, etc. },});Transaction Events
Section titled “Transaction Events”The transaction system emits events via hooks:
// Listen for successful persistencemanager.addHooks({ mutationsCommitted: ({ entries }) => { console.log("Persisted", entries.length, "operations"); },});
// Listen for errorsmanager.addHooks({ error: ({ error, context }) => { console.error(`Error during ${context}:`, error); // context is "initialization" | "pagination" | "mutation" },});Full Example: Programmatic CRUD
Section titled “Full Example: Programmatic CRUD”import type { CollectionManager } from "@blocknote/block-view/core";import { PropertyValue } from "@blocknote/block-view/core";
function performBulkOperations(manager: CollectionManager) { const transaction = manager.transaction; const collection = manager.collection; const view = manager.views[0];
// 1. Add a new column transaction.create("propertyId", collection, "due-date", { label: "Due Date", type: "date", });
// 2. Create a new document manager.createDocument({ viewId: view.id });
// 3. Update an existing document's value const doc = collection.documents.get("doc-1"); if (doc) { const statusValue = doc.getProp("status"); if (statusValue) { transaction.update(statusValue, { value: "done" }); } }
// 4. Delete a document const oldDoc = collection.documents.get("doc-to-remove"); if (oldDoc) { transaction.delete(oldDoc); }
// All changes are applied immediately in-memory // and will be persisted to sources after the debounce period}