Use the row bridge when your app owns the row objects and Sheetwrite owns editing, history, sorting, and filtering. The bridge is optional. Without getRowId, defaultRows keeps its existing one-time, uncontrolled behavior.
const entities = new Map < string , InvoiceRow >([
[ "invoice-1" , { id : "invoice-1" , customer : "Ada" , total : 120 }], [ "invoice-2" , { id : "invoice-2" , customer : "Lin" , total : 75 }], if ( delta . kind === "cell" ) { const { rowId , columnKey , next } = delta . cell ; if ( rowId === null || columnKey === null || next ?. kind !== "literal" ) continue ; const row = entities . get ( rowId );
if ( row ) entities . set ( rowId , { ... row , [ columnKey ]: next . value }); for ( const rowId of delta . removed ) if ( rowId !== null ) entities . delete ( rowId ); if ( rowId !== null ) entities . set ( rowId , { id : rowId , customer : "" , total : 0 });
Attach the same bridge and callback in any adapter:
} from "@sheetwrite/react" ;
declare const rows : readonly InvoiceRow [];
Vue emits row-delta; Svelte and the imperative controller use onRowDelta. All adapters call the same core bridge. Sorts, filters, and hidden rows only change the visible order, so emitted row IDs still refer to data-space rows.
Remote changes should enter through grid.applyRemoteOperations(...). Their projections have status: "remote" and source: "remote". An echoed local transaction is returned as duplicate with no deltas. For a server rejection or transformed acceptance, call bridge.reconcile(...) with the server result and the canonical applied operations. The bridge does not update your store automatically; your callback remains the only place that changes host rows.
By default, the bridge retains at most 8,192 recent transaction IDs and 8,192 recent local operation fingerprints. If your host applies its own echoes directly through applyRemoteOperations, deliver each echo before its identity leaves this window. An echo outside this window is no longer recognized and projects as a remote change. SyncCoordinator removes acknowledged local echoes before they become Grid change events, so its separate 4,096 acknowledgement window does not set the bridge limit.
Set maxRecentTransactions in createRowBridge to change the limit for each identity window. The value must be a positive safe integer; invalid values throw a RangeError. Raise the limit if your host can project more than 8,192 transactions before an echo arrives. A larger limit retains more identity strings and uses more memory.
function createRowBridge<Row extends Record<string, CellScalar>, Id extends RowBridgeId = RowBridgeId>(options: RowBridgeOptions<Row, Id>): RowBridge<Id> function createRowBridge <
Row extends Record < string , CellScalar >,
Id extends RowBridgeId = RowBridgeId ,
>( options : RowBridgeOptions < Row , Id >) : RowBridge < Id > function createRowBridge <
Row extends Record <
string ,
CellScalar
>,
Id extends RowBridgeId =
RowBridgeId ,
>(
options : RowBridgeOptions <
Row ,
Id
>,
) : RowBridge < Id > Create a typed bridge while preserving row and identity inference. API reference → interface RowBridgeProjection<Id extends RowBridgeId = RowBridgeId> interface RowBridgeProjection < Id extends RowBridgeId = RowBridgeId > interface RowBridgeProjection < Id extends RowBridgeId = RowBridgeId > Result of projection or reconciliation. API reference → InvoiceRow.id: string interface InvoiceRow {
id : string ;
} interface InvoiceRow {
id : string ;
} Stable identity used by operations, history, and collaboration rebase. API reference → InvoiceRow.customer: string interface InvoiceRow {
customer : string ;
} interface InvoiceRow {
customer : string ;
} InvoiceRow.total: number interface InvoiceRow {
total : number ;
} interface InvoiceRow {
total : number ;
} const entities: Map<string, InvoiceRow> const entities : Map < string , InvoiceRow > const entities : Map <
string ,
InvoiceRow
> var Map: MapConstructor
new <string, InvoiceRow>(iterable?: Iterable<readonly [string, InvoiceRow]> | null | undefined) => Map<string, InvoiceRow> (+3 overloads) var Map : MapConstructor
new < string , InvoiceRow >( iterable ?: Iterable < readonly [ string , InvoiceRow ]> | null | undefined ) => Map < string , InvoiceRow > ( + 3 overloads) var Map : MapConstructor
new < string , InvoiceRow >( iterable ?: Iterable < readonly [ string , InvoiceRow ]> | null | undefined ) => Map < string , InvoiceRow > ( + 3 overloads) interface InvoiceRow interface InvoiceRow interface InvoiceRow const rows: InvoiceRow[] const rows : InvoiceRow [] const rows : InvoiceRow [] end-exclusive row range API reference → Map<string, InvoiceRow>.values(): MapIterator<InvoiceRow> Map < string, InvoiceRow > . values (): MapIterator < InvoiceRow > Map < string, InvoiceRow > . values (): MapIterator < InvoiceRow > Returns an iterable of values in the map API reference → let nextId: number let nextId : number let nextId : number const bridge: RowBridge<`invoice-${number}`, Record<string, CellScalar>> const bridge : RowBridge <
`invoice-${ number }` ,
Record < string , CellScalar >
> const bridge : RowBridge <
`invoice-${ number }` ,
Record < string , CellScalar >
> createRowBridge<Record<string, CellScalar>, `invoice-${number}`>(options: RowBridgeOptions<Record<string, CellScalar>, `invoice-${number}`>): RowBridge<`invoice-${number}`, Record<string, CellScalar>> createRowBridge < Record < string , CellScalar >, `invoice-${ number }` >(options: RowBridgeOptions < Record < string, CellScalar > , `invoice-${ number }` > ): RowBridge < `invoice-${ number }` , Record < string, CellScalar >> createRowBridge < Record < string , CellScalar >, `invoice-${ number }` >(options: RowBridgeOptions < Record < string, CellScalar > , `invoice-${ number }` > ): RowBridge < `invoice-${ number }` , Record < string, CellScalar >> Create a typed bridge while preserving row and identity inference. API reference → RowBridgeOptions<Record<string, CellScalar>, `invoice-${number}`>.columns: readonly RowBridgeColumn<Record<string, CellScalar>>[] RowBridgeOptions < Record < string, CellScalar > , `invoice-${ number }` > .columns: readonly RowBridgeColumn < Record < string, CellScalar >> [] RowBridgeOptions < Record < string, CellScalar > , `invoice-${ number }` > .columns: readonly RowBridgeColumn < Record < string, CellScalar >> [] Exact column runs represented by every returned row. API reference → RowBridgeColumn<Record<string, CellScalar>>.key: string RowBridgeColumn < Record < string, CellScalar >> .key: string RowBridgeColumn < Record < string, CellScalar >> .key: string Non-empty key, unique within the sheet, used to map input and datasource values. API reference → RowBridgeOptions<Record<string, CellScalar>, `invoice-${number}`>.defaultRows: readonly Record<string, CellScalar>[] RowBridgeOptions < Record < string, CellScalar > , `invoice-${ number }` > .defaultRows: readonly Record < string, CellScalar > [] RowBridgeOptions < Record < string, CellScalar > , `invoice-${ number }` > .defaultRows: readonly Record < string, CellScalar > [] Rows converted to initial columnar data; missing keys become null. API reference → RowBridgeOptions<Record<string, CellScalar>, `invoice-${number}`>.getRowId: (row: Record<string, CellScalar>, index: number) => `invoice-${number}` RowBridgeOptions < Record < string, CellScalar > , `invoice-${ number }` > .getRowId: ( row : Record < string , CellScalar >, index : number ) => `invoice-${ number }` RowBridgeOptions < Record < string, CellScalar > , `invoice-${ number }` > .getRowId: ( row : Record < string , CellScalar >, index : number ) => `invoice-${ number }` Opt-in stable data-row identity extractor. API reference → row: Record<string, CellScalar> let row : Record < string , CellScalar > let row : Record <
string ,
CellScalar
> API reference → CellScalar CellScalar; CellScalar; Stable identity used by operations, history, and collaboration rebase. API reference → RowBridgeOptions<Record<string, CellScalar>, `invoice-${number}`>.createRowId?: ((context: RowBridgeInsertContext) => `invoice-${number}`) | undefined RowBridgeOptions < Record < string, CellScalar > , `invoice-${ number }` > .createRowId ?: (( context : RowBridgeInsertContext ) => `invoice-${ number }` ) | undefined RowBridgeOptions < Record < string, CellScalar > , `invoice-${ number }` > .createRowId ?: (( context : RowBridgeInsertContext ) => `invoice-${ number }` ) | undefined Optional identity factory for rows created by an addRows operation. API reference → function applyProjection(projection: RowBridgeProjection<string>): void function applyProjection (
projection : RowBridgeProjection < string >,
) : void function applyProjection (
projection : RowBridgeProjection < string >,
) : void API reference → projection: RowBridgeProjection<string> let projection : RowBridgeProjection < string > let projection : RowBridgeProjection < string > API reference → RowBridgeProjection<string>.status: RowBridgeReconciliationStatus interface RowBridgeProjection < string > {
status : RowBridgeReconciliationStatus ;
} interface RowBridgeProjection <
string ,
> {
status : RowBridgeReconciliationStatus ;
} RowBridgeProjection<string>.status: "accepted" | "transformed" | "out-of-order" | "duplicate" | "remote" interface RowBridgeProjection < string > {
status :
| "accepted"
| "transformed"
| "out-of-order"
| "duplicate"
| "remote" ;
} interface RowBridgeProjection <
string ,
> {
status :
| "accepted"
| "transformed"
| "out-of-order"
| "duplicate"
| "remote" ;
} API reference → const delta: RowBridgeDelta<string> const delta : RowBridgeDelta < string > const delta : RowBridgeDelta < string > API reference → RowBridgeProjection<string>.deltas: readonly RowBridgeDelta<string>[] interface RowBridgeProjection < string > {
deltas : readonly RowBridgeDelta < string >[];
} interface RowBridgeProjection <
string ,
> {
deltas : readonly RowBridgeDelta < string >[];
} kind: "cell" | "range" | "clear" | "paste" | "fill" | "row-structure" | "metadata" | "host-action" | "unprojectable" let kind :
| "cell"
| "range"
| "clear"
| "paste"
| "fill"
| "row-structure"
| "metadata"
| "host-action"
| "unprojectable" let kind :
| "cell"
| "range"
| "clear"
| "paste"
| "fill"
| "row-structure"
| "metadata"
| "host-action"
| "unprojectable" const rowId: string | null const rowId : string | null const rowId :
string | null const columnKey: string | null const columnKey : string | null const columnKey :
string | null const next: CellValue | undefined const next : CellValue | undefined const next :
CellValue | undefined API reference → const delta: RowBridgeCellDelta<string> const delta : RowBridgeCellDelta < string > const delta : RowBridgeCellDelta < string > RowBridgeCellDelta<string>.cell: RowBridgeCell<string> interface RowBridgeCellDelta < string > {
cell : RowBridgeCell < string >;
} interface RowBridgeCellDelta <
string ,
> {
cell : RowBridgeCell < string >;
} Right-clicked cell, or null when the pointer is outside the cell body. API reference → kind: "literal" | "ref" | "formula" | undefined let kind : "literal" | "ref" | "formula" | undefined let kind :
| "literal"
| "ref"
| "formula"
| undefined const row: InvoiceRow | undefined const row : InvoiceRow | undefined const row :
InvoiceRow | undefined Map<string, InvoiceRow>.get(key: string): InvoiceRow | undefined Map < string, InvoiceRow > . get (key: string): InvoiceRow | undefined Map < string, InvoiceRow > . get (key: string): InvoiceRow | undefined Returns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map. @returns Returns the element associated with the specified key. If no element is associated with the specified key, undefined is returned.const rowId: string const rowId : string const rowId : string Map<string, InvoiceRow>.set(key: string, value: InvoiceRow): Map<string, InvoiceRow> Map < string, InvoiceRow > . set (key: string, value: InvoiceRow): Map < string, InvoiceRow > Map < string, InvoiceRow > . set (key: string, value: InvoiceRow): Map < string, InvoiceRow > Adds a new element with a specified key and value to the Map. If an element with the same key already exists, the element will be updated. const row: InvoiceRow const row : InvoiceRow const row : InvoiceRow const columnKey: string const columnKey : string const columnKey : string const next: {
kind: "literal";
value: CellScalar;
} const next : {
kind : "literal" ;
value : CellScalar ;
} const next : {
kind : "literal" ;
value : CellScalar ;
} API reference → value: CellScalar let value : CellScalar let value : CellScalar API reference → const delta: RowBridgeRowStructureDelta<string> const delta : RowBridgeRowStructureDelta < string > const delta : RowBridgeRowStructureDelta < string > API reference → RowBridgeRowStructureDelta<string>.action: "insert" | "delete" | "move" interface RowBridgeRowStructureDelta < string > {
action : "insert" | "delete" | "move" ;
} interface RowBridgeRowStructureDelta <
string ,
> {
action :
"insert" | "delete" | "move" ;
} Built-in action to bind (or "separator"). Omit when supplying onClick. API reference → RowBridgeRowStructureDelta<string>.removed: readonly (string | null)[] interface RowBridgeRowStructureDelta < string > {
removed : readonly ( string | null )[];
} interface RowBridgeRowStructureDelta <
string ,
> {
removed : readonly (
string | null
)[];
} API reference → Map<string, InvoiceRow>.delete(key: string): boolean Map < string, InvoiceRow > . delete (key: string): boolean Map < string, InvoiceRow > . delete (key: string): boolean @returns true if an element in the Map existed and has been removed, or false if the element does not exist.RowBridgeRowStructureDelta<string>.inserted: readonly (string | null)[] interface RowBridgeRowStructureDelta < string > {
inserted : readonly ( string | null )[];
} interface RowBridgeRowStructureDelta <
string ,
> {
inserted : readonly (
string | null
)[];
} API reference → const SheetwriteGrid: <Id extends RowBridgeId = RowBridgeId>(props: SheetwriteGridProps<Id> & {
ref?: ForwardedRef<Grid>;
}) => ReactElement const SheetwriteGrid : <
Id extends RowBridgeId = RowBridgeId ,
>(
props : SheetwriteGridProps < Id > & {
ref ?: ForwardedRef < Grid >;
},
) => ReactElement const SheetwriteGrid : <
Id extends RowBridgeId =
RowBridgeId ,
>(
props : SheetwriteGridProps < Id > & {
ref ?: ForwardedRef < Grid >;
},
) => ReactElement Advanced framework component with inferred row-bridge identity. API reference → class RowBridge<Id extends RowBridgeId = RowBridgeId, Row extends Record<string, CellScalar> = Record<string, CellScalar>> class RowBridge < Id extends RowBridgeId = RowBridgeId , Row extends Record < string , CellScalar > = Record < string , CellScalar >> class RowBridge < Id extends RowBridgeId = RowBridgeId , Row extends Record < string , CellScalar > = Record < string , CellScalar >> Projects canonical document transactions into host-owned row changes. The bridge only owns compact data-space identity arrays. It never writes to defaultRows, never renders, and never creates a second document store. API reference → interface SimpleColumn<Row extends Record<string, CellScalar>> interface SimpleColumn < Row extends Record < string , CellScalar >> interface SimpleColumn < Row extends Record < string , CellScalar >> Column definition accepted by the adapters’ simple row-object API. API reference → const columns: readonly SimpleColumn<InvoiceRow>[] const columns : readonly SimpleColumn < InvoiceRow >[] const columns : readonly SimpleColumn < InvoiceRow >[] Exact column runs represented by every returned row. API reference → const rows: readonly InvoiceRow[] const rows : readonly InvoiceRow [] const rows : readonly InvoiceRow [] end-exclusive row range API reference → const bridge: RowBridge<string, Record<string, CellScalar>> const bridge : RowBridge < string , Record < string , CellScalar >> const bridge : RowBridge <
string ,
Record < string , CellScalar >
> columns: readonly SimpleColumn<Record<string, CellScalar>>[] let columns : readonly SimpleColumn <
Record < string , CellScalar >
>[] let columns : readonly SimpleColumn <
Record < string , CellScalar >
>[] Exact column runs represented by every returned row. API reference → const columns: readonly SimpleColumn<Record<string, CellScalar>>[] const columns : readonly SimpleColumn <
Record < string , CellScalar >
>[] const columns : readonly SimpleColumn <
Record < string , CellScalar >
>[] Exact column runs represented by every returned row. API reference → defaultRows: readonly Record<string, CellScalar>[] let defaultRows : readonly Record < string , CellScalar >[] let defaultRows : readonly Record <
string ,
CellScalar
>[] Rows converted to initial columnar data; missing keys become null. API reference → const rows: readonly Record<string, CellScalar>[] const rows : readonly Record < string , CellScalar >[] const rows : readonly Record <
string ,
CellScalar
>[] end-exclusive row range API reference → SheetwriteGridProps<RowBridgeId>.rowBridge?: RowBridge<RowBridgeId, Record<string, CellScalar>> | undefined interface SheetwriteGridProps < RowBridgeId > {
rowBridge ?:
RowBridge < RowBridgeId , Record < string , CellScalar >> | undefined ;
} interface SheetwriteGridProps <
RowBridgeId ,
> {
rowBridge ?:
| RowBridge <
RowBridgeId ,
Record < string , CellScalar >
>
| undefined ;
} Projects canonical changes to host-owned row identities. API reference → SheetwriteGridProps<RowBridgeId>.onRowDelta?: RowBridgeHandler<RowBridgeId> | undefined interface SheetwriteGridProps < RowBridgeId > {
onRowDelta ?: RowBridgeHandler < RowBridgeId > | undefined ;
} interface SheetwriteGridProps <
RowBridgeId ,
> {
onRowDelta ?:
| RowBridgeHandler < RowBridgeId >
| undefined ;
} Receives one accepted or reconciled row projection. API reference → const applyProjection: (projection: RowBridgeProjection<string>) => void const applyProjection : (
projection : RowBridgeProjection < string >,
) => void const applyProjection : (
projection : RowBridgeProjection < string >,
) => void API reference → SheetwriteGridProps<Id extends RowBridgeId = RowBridgeId>.height?: string | number | undefined interface SheetwriteGridProps <
Id extends RowBridgeId = RowBridgeId ,
> {
height ?: string | number | undefined ;
} interface SheetwriteGridProps <
Id extends RowBridgeId =
RowBridgeId ,
> {
height ?:
string | number | undefined ;
} Host height in CSS pixels for numbers or any CSS length string. API reference → createRowBridge<{
id: string;
total: number;
}, string>(options: RowBridgeOptions<{
id: string;
total: number;
}, string>): RowBridge<string, Record<string, CellScalar>> createRowBridge <{
id : string ;
total : number ;
}, string >( options : RowBridgeOptions <{
id : string ;
total : number ;
}, string >) : RowBridge < string , Record < string , CellScalar >> createRowBridge <{
id : string ;
total : number ;
}, string >( options : RowBridgeOptions <{
id : string ;
total : number ;
}, string >) : RowBridge < string , Record < string , CellScalar >> Create a typed bridge while preserving row and identity inference. API reference → RowBridgeOptions<{ id: string; total: number; }, string>.columns: readonly RowBridgeColumn<{
id: string;
total: number;
}>[] interface RowBridgeOptions <{ id : string ; total : number ; }, string > { columns : readonly RowBridgeColumn <{
id : string ;
total : number ;
}>[] } interface RowBridgeOptions <{ id : string ; total : number ; }, string > { columns : readonly RowBridgeColumn <{
id : string ;
total : number ;
}>[] } Exact column runs represented by every returned row. API reference → RowBridgeColumn<{ id: string; total: number; }>.key: "total" | "id" interface RowBridgeColumn <{ id : string ; total : number ; }> { key : "total" | "id" } interface RowBridgeColumn <{ id : string ; total : number ; }> { key : "total" | "id" } Non-empty key, unique within the sheet, used to map input and datasource values. API reference → RowBridgeOptions<{ id: string; total: number; }, string>.defaultRows: readonly {
id: string;
total: number;
}[] interface RowBridgeOptions <{ id : string ; total : number ; }, string > { defaultRows : readonly {
id : string ;
total : number ;
}[] } interface RowBridgeOptions <{ id : string ; total : number ; }, string > { defaultRows : readonly {
id : string ;
total : number ;
}[] } Rows converted to initial columnar data; missing keys become null. API reference → id: string let id : string let id : string Stable identity used by operations, history, and collaboration rebase. API reference → total: number let total : number let total : number RowBridgeOptions<{ id: string; total: number; }, string>.getRowId: (row: {
id: string;
total: number;
}, index: number) => string interface RowBridgeOptions <{ id : string ; total : number ; }, string > { getRowId : ( row : {
id : string ;
total : number ;
}, index : number ) => string } interface RowBridgeOptions <{ id : string ; total : number ; }, string > { getRowId : ( row : {
id : string ;
total : number ;
}, index : number ) => string } Opt-in stable data-row identity extractor. API reference → row: {
id: string;
total: number;
} let row : {
id : string ;
total : number ;
} let row : {
id : string ;
total : number ;
} RowBridgeOptions<Row extends Record<string, CellScalar>, Id extends RowBridgeId = RowBridgeId>.maxRecentTransactions?: number | undefined RowBridgeOptions < Row extends Record < string, CellScalar > , Id extends RowBridgeId = RowBridgeId > .maxRecentTransactions ?: number | undefined RowBridgeOptions < Row extends Record < string, CellScalar > , Id extends RowBridgeId = RowBridgeId > .maxRecentTransactions ?: number | undefined Maximum entries in each recent identity window; defaults to 8,192. Must be a positive safe integer. API reference →