Skip to content

createAutoForm

createAutoForm(plugin): <TTable, TNested>(__namedParameters) => Element

Defined in: packages/forms/src/auto-form.tsx:448

Create an AutoForm React component bound to a specific UI FormPlugin.

The returned component introspects a Drizzle table via introspectTable, builds validation via createResolver, and renders fields using the plugin’s components. Supports:

  • Create and edit modesmode="create" (default) starts empty, mode="edit" pre-fills from data.
  • Per-field overrides via FieldConfig: labels, placeholders, hidden, defaults, custom components, custom validation, readOnly, and computed.
  • Column exclusion via the exclude prop.
  • Nested parent-child tables via the nested prop. Each entry is introspected like the parent table and rendered as a useFieldArray with add / remove / reorder controls. The submitted value type is inferred from table + each nested table, so onSubmit is fully typed without manual generics or casts.
  • Computed fields via fields[name].computed. Computed fields are recalculated whenever any watched value changes (including nested rows) and pushed into the form via setValue.
  • External react-hook-form instances via the form prop.

FormPlugin

A FormPlugin providing the UI components for rendering.

A generic React component (AutoForm) that accepts AutoFormProps.

<TTable, TNested>(__namedParameters): Element

TTable extends Table<TableConfig<Column<any, object, object>>> = SQLiteTable<TableConfig>

TNested extends readonly NestedTableConfig[] | undefined = undefined

AutoFormProps<TTable, TNested>

Element

import { createAutoForm, createFormPlugin } from "@cfast/forms";
const plugin = createFormPlugin({ components: joyComponents });
const AutoForm = createAutoForm(plugin);
// Recipe + ingredients + computed totals — fully inferred, no casts.
<AutoForm
table={recipes}
nested={[
{
table: ingredients,
foreignKey: "recipeId",
min: 1,
reorderable: true,
},
]}
fields={{
totalCalories: {
computed: (values) =>
(values.ingredients as Array<{ calories: number; amount: number }> ?? [])
.reduce((sum, ing) => sum + (ing.calories * ing.amount) / 100, 0),
readOnly: true,
},
}}
onSubmit={async (values) => {
// values.title: string, values.ingredients: Array<InferRow<typeof ingredients>>
}}
/>