Skip to content

v0.5.0

Compare
Choose a tag to compare
@edmundhung edmundhung released this 17 Jan 21:15
· 372 commits to main since this release

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>
    )
}
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