Migrating to v6
RVF v6 is a ground-up rewrite of remix-validated-form, where some of the core design desicisions have been reconsidered and updated.
Overview
Decoupled from Remix
RVF no longer requires Remix. It can be with any flavor of React, but there's also an adapter specifically for Remix.
Simplified, more predictable API
Many of the idiosyncrasies of remix-validated-form have been improved upon. Several APIs have been removed, because they aren't necessary anymore.
More powerful
Even though the RVF api is simpler and more predictable, it's also a lot more powerful. There's also an additional escape hatch for advanced use-cases called state mode.
Better render optimizations
The RVF api now makes use of proxy-based access tracking to ensure that renders are only triggered when necessary.
Migrating
New package names
remix-validated-form
has been renamed to only RVF
, and has new package names.
- @rvf/react
- @rvf/remix
- @rvf/zod
- @rvf/yup
This has the benefit of making it possible to adopt RVF gradually, instead of migrating your entire app at once. You can install the RVF packages alongside your existing remix-validated-form packages and start using it for new code right away.
Hook-first API
The recommended, default API for RVF is now the useForm
hook, rather than ValidatedForm
.
However, ValidatedForm
is still supported and isn't going anywhere -- it even supports a render-prop now.
Removed "use outside of form" APIs
You can no longer pass a form id to RVF hooks in order to access the form context outside of a ValidatedForm
component.
RVF still supports putting inputs outside of your form
element, but it's supported naturally by the useForm
hook
rather than requiring extra API support. If you're using getInputProps
, RVF will automatically add the form
prop for you,
so you don't need to do anything special.
In remix-validated-form
, a common pattern was to use this feature in the parent of the form like this:
export const MyForm = () => {
const [value, setValue] = useControlledField("name", "myFormId");
return (
<ValidatedForm validator={myValidator} method="post" id="myFormId">
<MyInput
name="name"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
</ValidatedForm>
);
};
This is no longer supported or necessary. To migrate this situation, you could either switch to the useForm
hook,
or use the render prop API of ValidatedForm
.
Migrating
const MyForm = () => {
const form = useForm({
validator: myValidator,
method: "post",
});
return (
<FormProvider scope={form.scope()}>
<form {...form.getFormProps()}>
<MyInput
name="name"
value={form.value("name")}
onChange={(e) => form.setValue("name", e.target.value)}
/>
</form>
</FormProvider>
);
};
Deprecated hooks
These hooks are deprecated because they're no longer necessary with the new api. Each hook below includes a recipe for how you could re-implement it in userland using the new APIs.
Many of these relied on being used within the context of a ValidatedForm
component.
If you're using ValidatedForm
, that will still work. But if you're using useForm
directly,
you'll need to use a FormProvider
in order to provide the context for these hooks.
These recipes also support being directly passed a FormScope
instead of being rendered inside a FormProvider
.
useIsSubmitting
export const useIsSubmitting = (rvf?: FormScope<any>) =>
useFormScopeOrContext(rvf).formState.isSubmitting;
useIsValid
export const useIsValid = (rvf?: FormScope<any>) =>
useFormScopeOrContext(rvf).formState.isValid;
useControlField
export const useControlField = <T>(name: string, rvf?: FormScope<any>) => {
const form = useFormScopeOrContext(rvf);
const value: T = form.value(name);
const setValue = useCallback(
(value: T) => form.setValue(name, value),
[form, name],
);
return [value, setValue] as const;
};
useUpdateControlledField
export const useUpdateControlledField = (rvf?: FormScope<any>) => {
const form = useFormScopeOrContext(rvf);
return useCallback(
(name: string, value: any) => form.setValue(name, value),
[form],
);
};
setFormDefaults
is also deprecated, but there's no userland replacement. It will continue to work for now,
and will be removed in a future major version.
Reworked useFormContext
useFormContext
now returns a FormApi
.
Other breaking changes
-
Unmounting controlled fields no longer clears the value stored for that field. This should result in more predictable behavior and give you more control over your form state.
-
form.validate
now simply returns an object of validation errors, rather than an object with anerror
anddata
field. -
The
handleReceiveFocus
API no longer exists. To set the focus target for a controlled field, pass the aref
fromgetControlProps
oruseField().refs.controlled
to a focusable html element. -
There is no longer a dedicated
subaction
prop. You can add this to your forms manually with a hidden input, or you can use thervfFormId
field instead. ThervfFormId
field is set automtically when JS is enabled or when usingValidatedForm
, but you must render it withform.renderFormIdInput
when usinguseForm
. -
Validator.validateField
was deprecated and has now been removed. -
defaultValues
can no longer be configured using a flat object of string paths (e.g.defaultValues: { "foo.bar": "baz" }
). This feature was unintended and undocumented, but there were a few users who were using it. -
Field paths can no longer use dot notation for array indeces (e.g.
foo.0.bar
). This was unintended and undocumented, but there were a few users who were using it.