v0.5.0
Hey! I am glad you are here. There are many exciting changes on v0.5. Here is what's changed and a brief migration guide. If you would like to learn more about the new features, please checkout the new guides on the website.
Breaking Changes
- The
useForm
hook now returns the fieldset together as a tuple (#78)
// Before
export default function LoginForm() {
const form = useForm();
const { email, password } = useFieldset(form.ref, form.config);
return (
<form {...form.props}>
{/* ... */}
</form>
);
}
// After the changes
export default function LoginForm() {
const [form, { email, password }] = useForm();
return (
<form {...form.props}>
{/* ... */}
</form>
);
}
Tips: As there is no change on the form
objects. You can also do this to simplify the migration and fix the rest gradually:
export default function LoginForm() {
// Just find and replace `form = useForm` with `[form] = useForm`
const [form] = useForm();
const { email, password } = useFieldset(form.ref, form.config);
return (
<form {...form.props}>
{/* ... */}
</form>
);
}
- The
useFieldList
hook now returns a list of field error and config only. The command is available as an additional API instead. (#70)
// Before
import { useForm, useFieldset, useFieldList, conform } from '@conform-to/react';
function Example(config) {
const form = useForm();
const { tasks } = useFieldset(form.ref, form.config);
const [taskList, command] = useFieldList(form.ref, tasks.config);
return (
<form {...form.props}>
{taskList.map((task, index) => (
<div key={task.key}>
<input {...conform.input(task.config)} />
<button {...command.remove({ index })}>Add</button>
</div>
))}
<button {...command.append()}>Add</button>
</form>
)
}
// After
import { useForm, useFieldList, list, conform } from '@conform-to/react';
function Example(config) {
const [form, { tasks }] = useForm();
const taskList = useFieldList(form.ref, tasks.config);
return (
<form {...form.props}>
{taskList.map((task, index) => (
<div key={task.key}>
<input {...conform.input(task.config)} />
<button {...list.remove(tasks.config.name, { index })}>
Delete
</button>
</div>
))}
{/* All `list` commands require the name now, i.e. 'tasks' */}
<button {...list.append(tasks.config.name)}>Add</button>
</form>
)
}
Tips: The list
command builder can be used anywhere as long as you know about the name of the list.
import { useForm, useFieldList, list, conform } from '@conform-to/react';
function Example(config) {
const [form, { tasks }] = useForm();
const taskList = useFieldList(form.ref, tasks.config);
return (
<form {...form.props} id="todos">
{/* Nothing changed from above*/}
</form>
)
}
// On the sidebar (outside of the form)
function Sidebar() {
return (
<button {...list.append('tasks')} form="todos">Add Task</button>
);
}
Improvements
-
Conform now inserts placeholder buttons for error that have no matching elements, e.g. form error. This will not break any existing form with placeholder buttons, e.g.
<button name="..." hidden />
and could be removed gradually. (#69) -
File Upload is now supported natively including multiple file input. More details can be found here (#72)
-
The
useForm
API now accepts an optional form id which will be used to derive aria-attributes. More details can be found here. (#77) -
The
useFieldList
API now captures and returns the error for each item. (#71)
import { useForm, useFieldList, list, conform } from '@conform-to/react';
function Example(config) {
const [form, { tasks }] = useForm();
const taskList = useFieldList(form.ref, tasks.config);
return (
<form {...form.props}>
{taskList.map((task, index) => (
<div key={task.key}>
<input {...conform.input(task.config)} />
{/* Error of each task */}
<div>{task.error}</div>
</div>
))}
</form>
)
}
- Introduced a new API to execute command imperatively: requestCommand. (#70)
import {
useForm,
useFieldList,
conform,
list,
requestCommand,
} from '@conform-to/react';
import DragAndDrop from 'awesome-dnd-example';
export default function Todos() {
const [form, { tasks }] = useForm();
const taskList = useFieldList(form.ref, tasks.config);
// Execute a command with a form element and a list command
const handleDrop = (from, to) =>
requestCommand(form.ref.current, list.reorder({ from, to }));
return (
<form {...form.props}>
<DragAndDrop onDrop={handleDrop}>
{taskList.map((task, index) => (WW
<div key={task.key}>
<input {...conform.input(task.config)} />
</div>
))}
</DragAndDrop>
<button>Save</button>
</form>
);
}
- Similar to the new list command builder, the internal validate command builder is also exported now, which could be used to trigger validation manually. More details can be found here. (#84)
Full Changelog: v0.4.1...v0.5.0