An API for crafting Mantine powered forms.
You'll need to fully setup Mantine, following this guide.
yarn add @caldwell619/mantine-form-generator
The following will render a single text input with a label of "One".
- The
name
property must match one of your object keys. This is the same behavior as react-hook-form. - Each type of input has their own
config
. The specifics are determined by thetype
property. For example,select
requires you to passoptions
.
**You must add a provider that wraps your form FOR EACH FORM YOU USE. **
This is not shown in this example, but is shown in this one, with just the single form.
import { FC, useContext } from 'react'
import { MantineForm, Config, MantineFormContext } from '@caldwell619/mantine-form-generator'
import { Button } from '@mantine/core'
import { diff } from 'deep-object-diff'
import type { UseFormReturn } from 'react-hook-form'
export const defaultValues: SomeObject = {
one: 'Rex',
two: 'Cody',
three: 'Wolffe'
}
const inputs: Config<SomeObject>[] = [
{
type: 'text',
config: {
control: {
name: 'one',
label: 'One'
}
}
}
]
export const Form: FC = () => {
const { handleSubmit } = useContext<UseFormReturn<SomeObject>>(MantineFormContext)
const onSubmit = (data: SomeObject) => {
console.log('Current state of form', data)
}
return (
<form>
<MantineForm inputs={inputs} />
<Button variant='outlined' onClick={handleSubmit(onSubmit)}>
Submit
</Button>
</form>
)
}
export interface SomeObject {
one: string
two: string
three: string
}
The result is just a single input and your button under it. Clicking submit will console log an object showing your defaults:
{
one: 'Rex',
two: 'Cody',
three: 'Wolffe'
}
There is a working example with a select input and a text field that can be found here
Currently, there are only 2 supported inputs, but this list will grow with time.
- Single Checkbox
- Custom Overrides
- Radio
- Select
- Switch
- Text
- Multi Checkbox ( Select all that apply )
- Checkbox Radio ( Many options, can only choose one )
- Layout ( Useful for separating sections )
If an input you want is not supported, you can "easily" pass your own custom input into the render. For an example, see the Date override.
Some override examples
This is an example of using a Date picker, which is not supported natively by this tool, because they are so specific.
import { DateInput, DateInputProps } from '@mantine/dates'
import type { CustomOverrideRenderArgs } from '@caldwell619/mantine-form-generator'
import type { FieldValues } from 'react-hook-form'
import '@mantine/dates/styles.css'
export const DateFormInput = function <TData extends FieldValues>({
field: { name, value, onChange },
fieldState: { error, invalid },
dateInputProps
}: CustomOverrideRenderArgs<TData> & { dateInputProps?: DateInputProps }) {
return (
<DateInput {...dateInputProps} value={value} onChange={onChange} error={error?.message || invalid} name={name} />
)
}
import type { CustomOverrideRenderArgs } from '@caldwell619/mantine-form-generator'
import { Textarea, TextareaProps } from '@mantine/core'
import type { FieldValues } from 'react-hook-form'
export const TextareaFormInput = function <TData extends FieldValues>({
field: { name, value, onChange },
fieldState: { error, invalid },
textareaProps
}: CustomOverrideRenderArgs<TData> & { textareaProps?: TextareaProps }) {
return <Textarea {...textareaProps} error={error?.message || invalid} name={name} value={value} onChange={onChange} />
}
import type { CustomOverrideRenderArgs } from '@caldwell619/mantine-form-generator'
import { MultiSelect, MultiSelectProps } from '@mantine/core'
import type { FieldValues } from 'react-hook-form'
export const MultiSelectInput = function <TData extends FieldValues>({
field: { name, value, onChange },
fieldState: { error, invalid },
multiSelectProps
}: CustomOverrideRenderArgs<TData> & { multiSelectProps?: MultiSelectProps }) {
return (
<MultiSelect
{...multiSelectProps}
error={error?.message || invalid}
name={name}
value={value}
onChange={onChange}
/>
)
}
A layout example
Along the same line as a custom override, you can also use a layout
type. This is helpful for grouping form sections, or providing a label.
const spacerConfig: Config<SomeObject> = {
type: 'layout',
config: {
render() {
return <Space h='lg' />
}
}
}
const sectionName: Config<SomeObject> = {
type: 'layout',
config: {
render() {
return (
<GridCol>
<Title order={2} fz="xl">
Your Name
</Title>
</GridCol>
)
},
},
}
const inputs: Config<SomeObject>[] = [
sectionName,
spacerConfig
]
You may pass rules to each form component that act as validation. You can read more about the validation rules on react hook form under the "Register Options".
These rules are optional, and will be applied to the unit they are applied to. If none are given, it is assumed the input can be empty upon submission.
There is also validation in the example, here.
rules: {
required: { value: true, message: 'This is required' },
pattern: { value: /^[0-9]*$/, message: 'Must be a number' }
},
The bundle size is a bit deceptive, as the published version is un-minified JS. I haven't found the best way to go about this, but it seems as if the best way is to just provide the source, and let you bundle it.
However you React will also tree shake and minify this library. I'm seeing an average of 3-5kb depending on which inputs are used. This will be less if you are already using these inputs elsewhere in the bundle.
Sometime wrapping the consumer is tedious, you don't really need it at the next level, but it has to go somewhere.
Using withMantineForm
, you can access the form config from the same component. It's similar to using MantineFormContext.Consumer
, but a bit more convenient.
2 React components
import { UseFormReturn } from 'react-hook-form'
import { MantineFormContext, MantineForm } from '@caldwell619/mantine-form-generator'
const Form = () => {
const { handleSubmit } = useContext<UseFormReturn<SomeObject>>(MantineFormContext)
return (
<MantineForm inputs={inputs} gridSpacing={1} />
)
}
const WrappedForm: FC = () => {
return (
<MantineForm>
<Form>
</MantineForm>
)
}
Higher order wrapper
import { UseFormReturn } from 'react-hook-form'
import { withMantineForm, MantineFormContext, MantineForm } from '@caldwell619/mantine-form-generator'
export const Home = withMantineForm({ defaultValues }, () => {
const { handleSubmit } = useContext<UseFormReturn<SomeObject>>(MantineFormContext)
return <MantineForm inputs={inputs} gridSpacing={1} />
})