Skip to content

Submitting

When the user clicks Submit, Updog hands you the edited dataset grouped by source. You decide what to do with it — INSERT, UPDATE, DELETE, skip — by filtering on per-row flags.

Type(result: DataEditorResult<TRow>) => void | Promise<void>
type DataEditorResult<TRow> = {
sources: DataEditorSourceResult<TRow>[];
counts: {
new: number;
changed: number;
deleted: number;
invalid: number;
};
};

counts is an aggregate across every source.

type DataEditorSourceResult<TRow> = {
sourceId: string;
sourceName: string;
rows: ResultRow<TRow>[];
};

One entry per source. Pristine backend rows — unchanged, undeleted, not imported — are omitted. They’re no-ops.

type ResultRow<TRow> = {
row: TRow;
isNew: boolean;
isChanged: boolean;
isDeleted: boolean;
isValid: boolean;
};

The flags are orthogonal. A single row can be new + changed + deleted at once.

Updog doesn’t pre-group rows because routing depends on your backend — some clients upsert, some reject invalid rows, some keep deletions for audit.

onComplete={async (result) => {
for (const source of result.sources) {
const inserts = source.rows.filter(r => r.isNew && !r.isDeleted && r.isValid);
const updates = source.rows.filter(r => !r.isNew && r.isChanged && !r.isDeleted && r.isValid);
const deletes = source.rows.filter(r => r.isDeleted && !r.isNew);
await persist(source.sourceId, { inserts, updates, deletes });
}
}}

If you never tagged sources via loadData, you’ll get one entry with sourceId: "backend".