Async validation
Schemas that resolve asynchronously. Pending editor state, supersede on newer commits, and the batched bulk paths.
The demo's SKU column runs a fake uniqueness check with ~800 ms latency (a hand-rolled
Standard Schema whose validate returns a Promise: the same seam as an async Zod refine).
SKU-001 and SKU-002 are already taken.
- Age must be 18 or older (sync function)
- Email must contain an @ (sync Standard Schema)
- SKU must be unique — SKU-001 and SKU-002 are taken; the check takes ~800ms (async Standard Schema)
- Price must be a number — decimals commit as the rounded integer (transforming schema)
- Notes max 12 characters, SOFT (onInvalid: 'warn') — the value commits and the cell stays flagged; a short re-commit clears it
- Zod Score integer 0–100 — zod: z.number().int().min(0).max(100)
- Joi Code uppercase letters, digits, or dashes — joi: .pattern(/^[A-Z0-9-]+$/)
- Valibot Tag at least 3 characters — valibot: v.pipe(v.string(), v.minLength(3))
Type a value that breaks a rule and press Enter: the editor stays open with the message under the cell, and the Age rule also fires a toast (from inside its own validate function). Or copy a column of ages (some under 18) from any spreadsheet and paste it onto Age: valid cells commit, invalid ones drop. Notes is the opposite: onInvalid: "warn" commits the value anyway and flags the cell (hover it for the message).
pnpm dlx shadcn@latest add @gridcn/data-grid-validation-demo
Single-cell edit
Type a SKU and press Enter:
- The editor stays open while the schema resolves. The built-in text/number editors mark
their input
readOnlyas the pending treatment. Neverdisabled, so Escape still cancels. - Resolving with issues shows the rejection message exactly like a sync rejection. Editing continues.
- Resolving successfully commits the schema's
result.value, then applies the pending cell-to-cell movement. - Escape, a newer commit, or unmounting the cell invalidates the in-flight validation: its resolution is dropped silently and never applied late. That is the supersede rule: a stale verdict can never land on a cell the user has already moved on from.
Bulk paths
Paste, fill, CSV/XLSX import, and updateCells hold the batch while the schema resolves:
- The grid validates a maximum of 32 cells at the same time. A paste of 10,000 cells makes 32 concurrent calls, not 10,000.
- Nothing changes until every verdict is in. The accepted cells then commit together as one
onDataChangeoperations batch. - A rejected cell drops. One bad value never fails the batch. Paste and fill report it
through
onValidationErrorwhen you pass one. - A newer operation supersedes a held batch: a second paste over the same area, a data change, a sort/filter change, or unmounting the grid drops the older batch silently.
- The import dialog keeps its Import button disabled while it validates. Cancel or closing aborts the build.
A slow schema makes a slow batch
A held batch is only as fast as your schema: 200 ms per cell across 10,000 cells takes about
60 s at 32 concurrent calls, and the grid gives no per-cell progress. For a large paste
against a remote check, validate the block yourself with the processPaste override
(Clipboard): one request for the whole block.
Trusted feeds can skip validation
actions.updateCells(patches, { skipValidation: true }) bypasses validate completely. Use
it when the producer already validated the values.
Behavior reference
- Async schemas: the full contract, including the 32-way batching
- Server errors: the other kind of rejection: after commit