tp-form renders a native form in light DOM and coordinates the Fields and form-associated controls inside it. Submitting validates every Field, focuses the first invalid control and blocks submission until the errors are fixed. A valid submission calls onFormSubmit with the named values and prevents navigation; without a callback the native submission proceeds. The Form does not send anything itself, so transport and persistence stay in your code.
Put submit and reset Buttons in an element with slot="actions" to get the footer layout. Native attributes such as action, method and name are forwarded to the inner form.
no-autofill opts every contained text-entry control (Input, Text area, Select and its query, One-time code field) out of browser and password-manager autofill. Use it for forms about other people or for app data, not for the user’s own sign-in, address or payment details.
An optional container with slot="actions" receives the actions part; slot="error-summary" receives the summary part. Summary text and links remain application content. Unnamed fields validate without entering values; duplicate names aggregate in DOM registration order.
Control types
Every form-associated control takes part the same way: Select, Checkbox, Radio group and Switch validate required through their Field and submit under their name. A horizontal Field suits the Switch.
Notification settings
Default density
Compact density
Restart from the initial state
Show the copyable source
All activityMentions and repliesNothingI agree to the notification policy
ResetSave settings
All activityMentions and repliesNothingI agree to the notification policy
By default errors appear on submit. Set validationTiming to on-blur or on-change to validate earlier for every Field in the form; a Field’s own validation-mode still wins.
Validate on blur
Default density
Compact density
Restart from the initial state
Show the copyable source
Assign errors, a record of field name to message, to show results from your server. They stay until you replace them, so clear them when the user edits the value.
External errors
Default density
Compact density
Restart from the initial state
Show the copyable source
A validator may return a promise, but submission does not wait for it. To wait, cancel tp-submit and await event.detail.validationRun.completion before saving.
Wait for async checks
Default density
Compact density
Restart from the initial state
Show the copyable source
Save username
Save username
<tp-form id="form-demo-async" aria-label="Choose a username" style="max-inline-size:24rem">
<tp-field id="form-demo-async-field" label="Username" description="Try maya, which is already taken.">
<tp-input name="username" required default-value="maya"></tp-input>
</tp-field>
<div slot="actions"><tp-button type="submit">Save username</tp-button></div>
<output id="form-demo-async-output" style="color:var(--tp-muted-foreground);font-size:var(--tp-text-sm)"></output>
</tp-form>
<script type="module">
const form = document.querySelector('#form-demo-async');
const output = document.querySelector('#form-demo-async-output');
// Stands in for a request to your server.
document.querySelector('#form-demo-async-field').validator = (value) =>
new Promise((resolve) => {
setTimeout(() => resolve(value === 'maya' ? 'maya is already taken.' : null), 600);
});
form.addEventListener('tp-submit', async (event) => {
event.preventDefault();
output.textContent = 'Checking availability…';
const { validationRun, values } = event.detail;
const result = await validationRun.completion;
output.textContent = result.status === 'valid' ? `Saved @${values.username}.` : '';
});
</script>
Repeated fields
Fields can be added and removed while the form is open; each registers as it connects. Controls that share a name submit together, in document order.
Repeated emails
Default density
Compact density
Restart from the initial state
Show the copyable source
submissionPolicy can disable the submit Buttons while the form is invalid or while async validation is pending. Disabled actions are hard to discover, so prefer the default always-enabled unless the form is short and the requirements are obvious.
Pending policy disables only submit controls and restores only the disabled state that the policy applied. An authored disabled action stays disabled.
Disable while invalid
Default density
Compact density
Restart from the initial state
Show the copyable source
PropertyTypeDefaultDescriptionerrorsRecord<string, string | readonly string[]>—Errors from the server, keyed by Field name; each matching Field shows its messages until you
replace the record.onFormSubmit| ((
values: Record<string, unknown>,
details: {
sourceEvent: SubmitEvent;
form: HTMLFormElement;
data: FormData;
submitter: HTMLElement | null;
validationRun: ValidationRun | null;
},
) => void)
| undefined—Called with the named values on a valid submission instead of the native navigation; the second
argument carries the native form, its data, the submitter and the validation run.actionsread-onlyobject{ validate: (name?: string) => this.validate(name) }The Form's actions as plain functions, for passing validate to code that holds no reference
to the element.valuesread-onlyRecord<string, unknown>—The current values of the named Fields inside the Form; repeated names collect into a list.validationMode'on-submit' | 'on-blur' | 'on-change'—When the Fields inside validate unless they set their own: on submit, when a control loses
focus, or on every change.validationTiming'on-submit' | 'on-blur' | 'on-change'—An older name for validationMode, used when validation-mode is not set.nativeValidation'enabled' | 'suppressed'—Whether the browser's own constraint bubbles may show; suppressed leaves error display to the
Fields.submissionPolicy'always-enabled' | 'disable-while-invalid' | 'disable-while-pending'—When submit Buttons are disabled: never, while any Field is invalid, or while a validation is
pending.noAutofillboolean—Opts every contained text-entry control out of host and extension autofill.novalidateboolean—Skips native constraint validation on submit, as on a native form.formread-onlyHTMLFormElement | null—The native form element the Form renders its children into.
Events
EventTypeDescriptiontp-submitCustomEvent<{ form: HTMLFormElement; data: FormData; submitter: HTMLElement | null; validationRun: ValidationRun | null; values: Record<string, unknown>; reason: string; sourceEvent: SubmitEvent; }>Cancelable, after the Fields pass validation and before the native submission; detail.values holds the named values, with the native form, data, submitter and validationRun.tp-invalidCustomEvent<{ form: HTMLFormElement; validationRun: ValidationRun; reason: string; sourceEvent: Event; }>When a submission is blocked by an invalid Field or native constraint; detail.form, with the validationRun or the invalid control.tp-resetCustomEvent<{ form: HTMLFormElement; values: Record<string, unknown>; sourceEvent: Event; }>After a reset restores the controls; detail.values holds the restored values.
Methods
MethodReturnsDescriptionrequestSubmit(submitter?)voidValidates and submits as a press on the given submit control would, including a library Button.reset()voidRestores every control to its default value, as a native reset does.checkValidity()booleanWhether every native constraint in the form is satisfied, without showing errors.reportValidity()booleanChecks the native constraints and lets the browser report the first failure.validate(name?)ValidationRunValidates every Field, or only the one with the given name, and returns the run; a newer run
cancels this one.