Quickback Docs

resource-form

Create and edit form for any Quickback resource, driven by guards, column kinds, masking, and validation from the schema registry.

npx shadcn add @quickback/resource-form
import { ResourceForm } from "@/components/resource-form"

// Create
<ResourceForm table="jobs" onSuccess={(row) => navigate(`/jobs/${row.id}`)} />

// Edit, with a confirmed Delete
<ResourceForm table="jobs" id={jobId} onSuccess={close} onCancel={close} onDeleted={close} />

Props

PropTypeDescription
tablestringSchema-registry table name. Required.
idstringRecord id. Set it to edit that record (the form loads it first). Leave it out to create a new one.
onSuccess(row) => voidCalled with the saved record. The API's bare record, not an envelope.
onCancel() => voidShows a Cancel button when set.
onDeleted(id) => voidEdit mode. Shows a Delete button that asks for confirmation inline, then calls this. See Delete for when the button is hidden.

Which fields appear

The form shows exactly the fields the API accepts:

  • Create shows guards.createable. Edit shows guards.updatable.
  • These fields are read-only, with persistent helper text saying why: guards.immutable fields on edit, guards.protected fields ("Updated via actions only"), and masked fields the caller's roles can't see unmasked.
  • The form needs the table's create (create mode) or update (edit mode) route. Without it, submitting fails with "<table>" has no create route.

Controls

ColumnControl
inputHints entry textarea / richtextTextarea
Enum validationSelect of the allowed values
FK (.references())Searchable combobox over the target table (see below)
boolCheckbox
timestamp / dateDate-time / date input
decimalText input. q.decimal() is an exact decimal string, and a number input would round it.
json or arrayTextarea (JSON)
int, bigint, smallint, realNumber input, with min / max from validation
urlURL input
email validationEmail input
anything elseText input

Constraints show as persistent helper text under the field (Up to 200 characters · Optional), never as placeholder text.

FK lookups query the target table as you type (?search=, debounced) and load more rows by cursor on demand, so there is no first-page cap. When the target declares named views, it must set read.defaultView. Without it the field is disabled with <Target> needs read.defaultView to be looked up.

Validation and errors

  • Before submit, the form checks the registry's validation block: required (NOT NULL without a default on create, NOT NULL on edit), minLength / maxLength, min / max, whole numbers for integer columns, enum membership, URL, email, and pattern.
  • On submit, create sends every non-blank value. Edit sends only the fields you changed. A field you cleared is sent as null, which clears it. It is never sent as "".
  • Server errors show the problem's detail above the form. Per-field messages from QuickbackError.fields are shown under their fields.

Delete

The Delete button renders only when all of these hold: onDeleted is set, the table has a delete route, and the caller's roles don't plainly fail its access.roles. It asks for confirmation before calling the API.

On this page