Skip to main content

FormField

Layout wrapper that pairs a label, helper text, error message, and hint with any form control.

Quick start
import { FormField } from "mates-ui";

Basic

import { html, atom, renderApp } from 'mates';
import { FormField, InputField } from 'mates-ui';

const App = () => {
const name = atom("");
const email = atom("");
return () => html`
<x-col gap="var(--md-space-5)" style="max-width:24rem;width:100%;">
${FormField({
label: "Full name", labelFor: "pg-name",
helper: "Enter your first and last name.",
control: InputField({ id: "pg-name", placeholder: "Jane Doe", value: name(), onInput: (e) => name.set(e.target.value) }),
})}
${FormField({
label: "Email address", labelFor: "pg-email",
helper: "We'll never share your email.",
control: InputField({ id: "pg-email", type: "email", placeholder: "jane@example.com", value: email(), onInput: (e) => email.set(e.target.value) }),
})}
</x-col>
`;
};

renderApp(App, document.getElementById('app'));

Label positions

import { html, atom, renderApp } from 'mates';
import { Checkbox, FormField, Text } from 'mates-ui';

const App = () => {
const top = atom(false);
const start = atom(false);
const end = atom(false);
return () => html`
<x-col gap="var(--md-space-5)" style="max-width:28rem;width:100%;">
<x-col gap="var(--md-space-2)">
${Text("top (default)", { type: "label-sm", color: "muted" })}
${FormField({ label: "Subscribe to newsletter", labelPosition: "top", control: Checkbox({ checked: top(), onChange: (e) => top.set(e.target.checked) }) })}
</x-col>
<x-col gap="var(--md-space-2)">
${Text("start", { type: "label-sm", color: "muted" })}
${FormField({ label: "Subscribe to newsletter", labelPosition: "start", control: Checkbox({ checked: start(), onChange: (e) => start.set(e.target.checked) }) })}
</x-col>
<x-col gap="var(--md-space-2)">
${Text("end", { type: "label-sm", color: "muted" })}
${FormField({ label: "Subscribe to newsletter", labelPosition: "end", control: Checkbox({ checked: end(), onChange: (e) => end.set(e.target.checked) }) })}
</x-col>
</x-col>
`;
};

renderApp(App, document.getElementById('app'));

Error state

import { html, atom, renderApp } from 'mates';
import { FormField, InputField } from 'mates-ui';

const App = () => {
const email = atom("");
return () => html`
<x-col style="max-width:24rem;width:100%;">
${FormField({
label: "Email address", labelFor: "pg-err-email",
error: email().length > 0 && !email().includes("@") ? "Please enter a valid email address." : false,
control: InputField({ id: "pg-err-email", type: "email", placeholder: "jane@example.com", value: email(), onInput: (e) => email.set(e.target.value) }),
})}
</x-col>
`;
};

renderApp(App, document.getElementById('app'));

Required

import { html, atom, renderApp } from 'mates';
import { Checkbox, FormField, InputField } from 'mates-ui';

const App = () => {
const name = atom("");
const agreed = atom(false);
return () => html`
<x-col gap="var(--md-space-5)" style="max-width:24rem;width:100%;">
${FormField({
label: "Full name", labelFor: "pg-req-name", required: true,
helper: "This field is required.",
control: InputField({ id: "pg-req-name", placeholder: "Jane Doe", value: name(), onInput: (e) => name.set(e.target.value) }),
})}
${FormField({
label: "I agree to the terms", labelPosition: "end", required: true,
control: Checkbox({ checked: agreed(), onChange: (e) => agreed.set(e.target.checked) }),
})}
</x-col>
`;
};

renderApp(App, document.getElementById('app'));

Hint

import { html, atom, renderApp } from 'mates';
import { FormField, InputField } from 'mates-ui';

const App = () => {
const key = atom("");
const user = atom("");
return () => html`
<x-col gap="var(--md-space-5)" style="max-width:24rem;width:100%;">
${FormField({
label: "API key", labelFor: "pg-hint-api",
hint: "Your API key is visible in your account settings. Keep it secret.",
control: InputField({ id: "pg-hint-api", placeholder: "sk-…", value: key(), onInput: (e) => key.set(e.target.value) }),
})}
${FormField({
label: "Username", labelFor: "pg-hint-user",
hint: html`<span style="font-size:var(--md-text-xs);color:var(--md-color-text-muted);">Only letters, numbers, and underscores. Max 30 chars.</span>`,
control: InputField({ id: "pg-hint-user", placeholder: "jane_doe", value: user(), onInput: (e) => user.set(e.target.value) }),
})}
</x-col>
`;
};

renderApp(App, document.getElementById('app'));

Disabled

import { html, renderApp } from 'mates';
import { Checkbox, FormField, InputField } from 'mates-ui';

const App = () => () => html`
<x-col gap="var(--md-space-5)" style="max-width:24rem;width:100%;">
${FormField({
label: "Account email", labelFor: "pg-dis-email",
helper: "Contact support to change your email.",
disabled: true,
control: InputField({ id: "pg-dis-email", value: "locked@example.com", disabled: true }),
})}
${FormField({
label: "Receive marketing emails", labelPosition: "end", disabled: true,
control: Checkbox({ checked: false, disabled: true }),
})}
</x-col>
`;

renderApp(App, document.getElementById('app'));

FormField

import { FormField } from "mates-ui";
Prop Type Default Description
attr AttrMap HTML attributes on the root element.
classes string | string[] Extra root classes — joined and appended after built-in classes.
control* TemplateResult The form control to wrap — InputField, Checkbox, etc.
data DataAttrMap Root `data-*` map (`testId` → `data-testid`). `testId` prop / `attr` win over `data`.
disabled boolean false Dims the field and label.
error string | boolean Error message (string) or error state flag (boolean). String shows as red text below.
helper string Muted helper text shown below the control.
hint string | TemplateResult String: info icon with tooltip. TemplateResult: block rendered below the label row.
id string Root `id` shorthand (`attr.id` wins). Form native-control `id` props are unchanged.
label string Label text rendered above (or beside) the control.
labelFor string id of the control element — wires the <label> for= attribute.
labelPosition "top" | "start" | "end" "top" top = above, start = left of control, end = right of control.
moreInfoLabel string "More information" Aria-label for the string-hint info icon.
on OnEventMap DOM event handlers on the root element.
required boolean false Appends a * marker to the label.
style StyleMap Inline CSS on the root element.