Store rrweb event data
rrweb emits an ordered event stream. The Library does not choose a database, upload transport, retention policy, or access-control model; your application owns those storage and delivery decisions.
Preserve event order
Store each event with its rrweb timestamp and preserve the order in which the recorder emitted it. Timestamps drive replay timing, while a stable sequence number is useful when multiple events share a timestamp or batches arrive out of order. Do not sort only by upload time.
Batch uploads without losing events
Batching reduces request overhead, but a failed batch must be retried without losing events. Give batches or individual events stable identifiers so the receiver can make retries idempotent. A retry should be lossless: accepting the same batch twice must not duplicate events, and acknowledging a batch must mean it is durably stored.
Start with a minimal storage shape
Send batches in an envelope that lets the receiver preserve order and recognize retries:
type EventBatch = {
recordingId: string;
batchId: string;
sequenceStart: number;
events: unknown[];
};On the server, store recordingId, batchId, sequenceStart, and the event payload in one durable transaction. Add a unique constraint on (recordingId, batchId), acknowledge only after the transaction commits, and return the existing acknowledgement when the same batch is retried. Load batches by sequenceStart, then concatenate their events without re-sorting events inside a batch.
This is a transport and persistence contract, not a complete production schema. Your application still needs authenticated writes and reads, request-size limits, compression, quotas, retention jobs, and deletion that covers payloads, indexes, and backups.
Keep full snapshots available
Replay cannot start from incremental events alone; every replayable segment needs the full snapshot that begins it.
If you split a long recording into segments, keep track of which full snapshot starts each replayable segment. Replayer checkout also depends on having a suitable full snapshot available before the requested point, so deleting or paging snapshots independently can make later events unusable.
Compress recordings
Compressing a whole-session payload usually produces a better ratio than compressing each event separately because the compressor can reuse patterns across the recording. The rrweb per-event packer is useful when events need to remain individually addressable, but it is not a substitute for transport or whole-session compression. See Optimize storage for the canonical Library recipe.
Plan retention and deletion
Define retention from the data's purpose and privacy requirements, then delete expired recordings and related indexes, metadata, snapshots, and backups together. Apply the same access-control model to stored events and retrieval endpoints, and make user-requested deletion auditable so a recording cannot be reconstructed from forgotten copies.
Load recordings for replay
Return events in their original order, including the full snapshot that initializes the requested range.
Retrieval is a replay concern, not only a storage query. Also make pagination boundaries stable while new data arrives. The Pagination recipe explains how to add later event batches to an active replayer.
Use rrweb Cloud instead
rrweb Cloud is the hosted alternative when you do not want to design and operate ingestion, storage, retention, access control, and replay delivery yourself.