Skip to main content
TapForm renders the same schema your manifest already declares — for a settings panel, an action’s input, or a credential set — as a form in the host’s styling, validates on submit, and hands you a typed value.

Props

What each schema construct renders

Submit drops the fields left blank, then checks what remains against the whole schema, so a minimum, a pattern or a maxLength you declared is enforced too and its message lands under the control it belongs to. formFields(schema) returns the same list TapForm renders, as { name, label, kind, required, hint, options }, when you want the fields without the form.

An action behind a form

An action goes through an approval card, so the form collects the input and the host does the rest:
tap.actions.run posts the values, and Tappify draws the approval card — the scope, the blast radius, whether it can be undone and the cost — before anything runs. See Add actions. A form with no value has nothing to infer T from, so annotate the handler’s parameter — ActionInput<"send-push"> here, TapSettingsValues in a settings panel. onSubmit returns nothing, so hand the promise to void rather than returning it. Point TapForm at the same file your manifest references. When the schema changes, tappify extension types changes the type in the same step, and a form that no longer matches its handler stops compiling.