Skip to content

Architecture

MLForm keeps five jobs separate:

PieceImportOwns
Kitmlform/kitApplication mounting, default UI wiring, layout helpers.
Runtimemlform/runtimeState, validation, conditions, submit flow, report state.
Schemamlform/schemaField and report contracts shared by UI and backends.
Primitivesmlform/primitivesWeb Components that render fields, reports, errors, and submit controls.
Design systemmlform/designThemes, recipes, tokens, host attachment.

Most app code starts with mountForm() from the kit.

import { mountForm } from "mlform/kit";
import type { FormSchema } from "mlform/schema";
import { createJsonTransport } from "mlform/transport";
const schema: FormSchema = {
fields: [{ id: "prompt", kind: "text", label: "Prompt" }],
};
mountForm(container, {
schema,
transport: createJsonTransport({ endpoint: "/predict" }),
});

Use createFormView() when MLForm should keep state and validation, but your app owns the visible layout. Use createForm() from runtime when there is no kit UI at all.

The important boundary: schema says what the form means, layout says how it is arranged, transport says where submitted values go, primitives say which UI pieces render the field and report descriptors.