conform

A type-safe form validation library utilizing web fundamentals to progressively enhance HTML Forms with full support for server frameworks like Remix and Next.js.

MIT License

Downloads
1M
Stars
1.9K
Committers
25

Bot releases are visible (Hide)

conform - v0.4.0-pre.1

Published by edmundhung almost 2 years ago

What's Changed

Full Changelog: https://github.com/edmundhung/conform/compare/v0.4.0-pre.0...v0.4.0-pre.1

conform - v0.4.0-pre.0

Published by edmundhung about 2 years ago

What's Changed

Full Changelog: https://github.com/edmundhung/conform/compare/v0.3.1...v0.4.0-pre.0

conform - v0.3.1

Published by edmundhung about 2 years ago

What's Changed

Conform guide (https://conform.guide)

This includes updated examples and a new Get started guide covering the core ideas behind conform.
I hope you will find it useful 😅

Autofocus first error field

Conform now automatically focus on first error field whenever user tries to initiate a form submission.
This applies to all native input fields with no changes required.

However, if you are working with controlled inputs and you want to have the invalid fields focused as well, you will need to pass the ref object provided by useControlledInput() to the input.

For example, you need to pass the ref object as inputRef with material-ui:

export default function ArticleForm() {
  const formProps = useForm();
  const { category } = useFieldset<Article>(formProps.ref);
  const [categoryInput, control] = useControlledInput(category.config);

  return (
    <form {...formProps}>
      <Stack spacing={3}>
        <input {...categoryInput} required />
        <TextField
          label="Category"
          inputRef={control.ref}
          value={control.value}
          onChange={control.onChange}
          onBlur={control.onBlur}
          error={Boolean(category.error)}
          helperText={category.error}
          inputProps={{
            // To disable browser report caused by the required
            // attribute set by mui input
            onInvalid: control.onInvalid,
          }}
          select
          required
        >
          <MenuItem value="">Please select</MenuItem>
          <MenuItem value="a">Option A</MenuItem>
          <MenuItem value="b">Option B</MenuItem>
          <MenuItem value="c">Option C</MenuItem>
        </TextField>
        <Button type="submit" variant="contained">
          Submit
        </Button>
      </Stack>
    </form>
  );
}

You can check out the full example here

New Contributors

Thank you!

Full Changelog: https://github.com/edmundhung/conform/compare/v0.3.0...v0.3.1

conform - v0.3.0

Published by edmundhung about 2 years ago

What's Changed

New packages

  • Conform now provides an official schema resolver for yup (#15)

Breaking Changes

  • useControlledInput now returns the props to be applied on the input element instead of giving you an element directly (#19)
import { useFieldset, useControlledInput } from '@conform-to/react';
import { Select, MenuItem } from '@mui/material';

function MuiForm() {
  const [fieldsetProps, { category }] = useFieldset(schema);
  const [inputProps, control] = useControlledInput(category);

  return (
    <fieldset {...fieldsetProps}>
      {/* Render a shadow input somewhere */}
      <input {...inputProps} />

      {/* MUI Select is a controlled component */}
      <Select
        label="Category"
        value={control.value}
        onChange={control.onChange}
        onBlur={control.onBlur}
        inputProps={{
          onInvalid: control.onInvalid
        }}
      >
        <MenuItem value="">Please select</MenuItem>
        <MenuItem value="a">Category A</MenuItem>
        <MenuItem value="b">Category B</MenuItem>
        <MenuItem value="c">Category C</MenuItem>
      </TextField>
    </fieldset>
  )
}
  • useFieldList now expects a form / fieldset ref and support form reset event properly (#20)
  • useForm no longer accept the onReset property. Just use the form onReset event listener if needed (#21)
  • useFieldset no longer returns the FieldsetProps, instead it expect a ref object of the form or fieldset element (#21)
  • The field information returned from useFieldset now groups all of the data as config except error as it is a field state (#21)
  • The parse function exported from @conform-to/zod is now merged with resolve (#22)
  • Introduced a new helper ifNonEmptyString for zod schema preprocess configuration (#24)
import { z } from 'zod';
import { resolve, ifNonEmptyString } from '@conform-to/zod';

const schema = resolve(
  z.object({
    // No preprocess is needed for string as empty string
    // is already converted to undefined by the resolver
    text: z.string({ required_error: 'This field is required' }),

    // Cast to number manually
    number: z.preprocess(
      ifNonEmptyString(Number),
      z.number({ required_error: 'This field is required' }),
    ),

    // This is how you will do it without the helper
    date: z.preprocess(
      (value) => (typeof value === 'string' ? new Date(value) : value),
      z.date({ required_error: 'This field is required' }),
    ),
  }),
);

Improvements

Full Changelog: https://github.com/edmundhung/conform/compare/v0.2.0...v0.3.0

conform - v0.2.0

Published by edmundhung over 2 years ago

What's changed

Breaking changes

  • Removed the auto type casting preprocess setup on @conform-to/zod to enable full control over the form data parsing logic. (#5)
// Before:
const schema = z.object({
  number: z.number(), // '1' was casted to 1 automatically
  boolean: z.boolean(), // Only checkbox with 'on' value was supported
});

// To achieve the same behaviour now:
const schema = z.object({
  number: z.preprocess(value => typeof value !== 'undefined' ? Number(value) : undefined, z.number()) 
  boolean: z.preprocess(value => value === 'on', z.boolean()) 
});
  • The result of parse is redesigned (#13)
// New type to represent current form state
interface FormState<T> {
  value: FieldsetData<T, string>;
  error: FieldsetData<T, string>;
}

// New name of `FormResult`
interface Submission<T> {
  // `processed` is renamed to `modified` 
  state: 'accepted' | 'rejected' | 'modified';
  // Only available if state = accepted
  data: T;
  // This will be always available regardless of state
  form: FormState<T>;
}
// Example usage
let action = async ({ request }) => {
  const formData = await request.formData();
  const submission = parse(formData, schema);

  if (submission.state !== 'accepted') {
    return json(submission.form);
  }

  // ... do something else
};

export default function RandomForm() {
  const formState = useActionData<FormState<Schema>>();
  const formProps = useFieldset(schema, {
    defaultValue: formState.value,
    error: formState.error,
  });

  // ....
}
  • Renamed initialValue to defaultValue. This affects several places including the options on the useFieldset hook (#11)
const [fieldsetProps, { a, b, c }] = useFieldset(schema, {
  // Before:
  // initialValue: ...
  
  // Now:
  defaultValue: ...
})
  • The FieldConfig type is renamed to FieldProps with a flattened structure (#12)
// Before
export interface FieldType<T> {
  name: string;
  initialValue?: FieldsetData<T, string>;
  error?: FieldsetData<T, string>;
  form?: string;
  constraint?: Constraint;  
}

// Now
export interface FieldProps<T> extends Constraint {
  name: string;
  defaultValue?: FieldsetData<T, string>;
  error?: FieldsetData<T, string>;
  form?: string;
}

New features

  • Added new list control commands (#14)
const [fieldList, control] = useFieldList(fieldProps);

// To append a new row (New: with optional defaultValue)
<button {...control.append(defaultValue)}>Append</button>;

// To prepend a new row (New: with optional defaultValue)
<button {...control.prepend(defaultValue)}>Prepend</button>;

// New: To replace a row with another defaultValue
<button {...control.replace(index, defaultValue)}>Replace</button>;

// New: To reorder a particular row to an another index
<button {...control.reorder(fromIndex, toIndex)}>Reorder</button>;

Improvements

  • Fixed a case with error report happens before the field is re-validated (#6)
  • Error messages are now cleared properly when the form reset (#7)
  • Fixed a issue with conform.input() incorrectly ignored the multiple property (#8)
  • Improved the constraint inferred from zod schema (#9)
  • The APIs of the react adapter and zod schema resolver is now documented (#10)

Full Changelog: https://github.com/edmundhung/conform/compare/v0.1.1...v0.2.0

conform - v0.1.1

Published by edmundhung over 2 years ago

What's Changed

Full Changelog: https://github.com/edmundhung/conform/compare/v0.1.0...v0.1.1

conform - v0.1.0

Published by edmundhung over 2 years ago