Mantine Form Skill
Written for Mantine 9.x.
Core Workflow
1. Set up the form
Uncontrolled mode is the recommended mode for all forms. The default is 'controlled', so set it explicitly.
2. Wire inputs with getInputProps
In uncontrolled mode every input needs key={form.key('path')}. Without it the input does not
update after form.setFieldValue, form.setValues or form.reset.
Checkboxes and switches need { type: 'checkbox' }:
Select, NumberInput, Radio.Group, Checkbox.Group and other Mantine inputs with a value /
onChange pair work with plain getInputProps and a key:
For standalone radios without Radio.Group, use form.getInputProps('color', { type: 'radio', value: 'red' })
and add key={form.key('color')} to each radio.
Components without an error prop (Slider, RangeSlider, Rating, SegmentedControl, Chip.Group)
need { withError: false }, and you render the error yourself:
3. Handle submission
onSubmit only calls the handler when validation passes. If the handler returns a promise,
form.submitting is true until it settles. To handle failures:
Validation
Rules object (most common)
Schema (zod, valibot, arktype and other Standard Schema libraries)
schemaResolver is built in: no resolver package is needed. Pass { sync: true } for
synchronous schemas so that form.validate() returns a plain result instead of a Promise.
Function (for cross-field logic)
When to validate
By default fields are validated on submit only.
With validation on blur, an error that appears when the user presses the submit button shifts the layout, and the click can miss the button. Keep the submit button in a place that does not move when errors appear (for example a footer with fixed position), or validate on change instead.
Modes
What rerenders in uncontrolled mode
- Typing in an input does not rerender the form.
form.getValues()during render is not updated by typing. To show or hide part of the form based on a value, useform.useWatchValue(path). Nested paths work:form.useWatchValue('members.0.role').setFieldValue,setValues,insertListItem,removeListItem,reorderListItem,resetandinitializedo rerender, so rendering a list fromform.getValues().items.map(...)is correct.form.errors,form.isDirty(),form.isDirty('path'),form.isTouched(),form.submittingandform.validatingcan be used during render in both modes: the form rerenders when they change.
References
For anything beyond a basic form, read both references before writing code:
references/patterns.md[blocked] — read for: reusing one form for different records (edit dialogs), focusing the first invalid field, more than one submit button, changing a value while the user types, nested objects, array fields, lists inside lists andformRootRule, async validation (rules and async schemas), conditional fields, conditional validation, multi-step forms, loading initial values from a server, saving and setting a new baseline, controlling a form from outside its component, custom inputs, form context across components,transformValues, standaloneuseField, server errors after submissionreferences/api.md[blocked] — read for everything else. It is the only place that lists everyuseFormoption and return member (watch,onValuesChange,enhanceGetInputProps,resetField,clearFieldError,touchTriggerand others), plususeField,createFormContext,createFormActions,schemaResolver, built-in validators and types
Looking things up
If the references do not cover what you need, do not guess:
- If the Mantine MCP server (
@mantine/mcp-server) is connected, usesearch_docsandget_item_doc - Otherwise fetch
https://mantine.dev/llms.txtand open the linked form pages


