Beta: RVF version 6 #364
Replies: 24 comments 66 replies
-
I'm happy that you're working on a new version of this library. It's the way to go for me when dealing with forms in Remix. I would just have one general suggestion - keep it simple :). I had a few problems with rvf in the past. One issue that I remember is non-standard behavior of the onSubmit on the ValidatedForm component. I would match prefer to deal with the standard things in React and connect here and there stuff that rvf gives me. I also don't like that Remix introduced it's own Form component as well but I guess it was necessary. Also when I saw that Anyway, thanks for working on that and I'm sure it will be great library! |
Beta Was this translation helpful? Give feedback.
-
Awesome that you're working on a new version. My biggest grievance so far, which is really just a nitpick: I like to create standardized input components in my projects as part of a UI framework. Part of those components include error reporting, so I use Like @lukejagodzinski, I would also prefer if things are simple. No matter how many times I try, I can never wrap my head around react-hook-forms. Thanks so much for your hard work, it's a great library! |
Beta Was this translation helpful? Give feedback.
-
Interested to see the progress here. I've been championing this library ever since I found it, it blows all other Remix form packages out of the water. I loved I've built some very complex forms with this library (e.g. multi-nested dynamic arrays w/ many controlled inputs). Any problems I've run into have been easy to solve because it's all just basic HTML forms. This package is a truly remix package, it's driving missions are aligned with the web. That said, your proposal here suggests you are moving away from web fundamentals in order to capture the broader React market (i.e. not remix). The beauty of this library is that it simply provides handles into the DOM (just. like. Remix.), giving power rather than taking it. Anyone who needs even more control doesn't have to abandon this package, they can simply add some more of their own handles. This isn't true for I absolutely love this library. I love to see continued thought thrown into this package. I'm wondering if a better path forward is to simply increase the handles, clean them up, and make them EVEN MORE extendable. Inversion of control is a guiding Remix principle, and I this package already does just that. We would loose some of that with a move towards state managed forms. This package, along with Remix, is the way of the future. Next.js and React 19 are trying their hardest to get back to the basics, and Remix + RVF is light years ahead of them both. |
Beta Was this translation helpful? Give feedback.
-
Something else that would be nice: deeply nestable forms using arbitrary combinations of arrays and objects. |
Beta Was this translation helpful? Give feedback.
-
I am really excited about this rewrite. I've been using RVF since 2021 and the of visible activity on it made me wonder if I was going to need to eventually egress to another solution. Mind you RVF hasn't been broken but just had some API topologies which I were more efficient which seems to be getting addressed now. I am emphatic by the Do you think we'll get an ability to use a store adapter so that we can utilize our own store? |
Beta Was this translation helpful? Give feedback.
-
I cannot wait until these go away! |
Beta Was this translation helpful? Give feedback.
-
Alpha versions of the v6 packages have been published 🎉 If anyone would like to try them out to see if anything is broken (there probably is), that would be super helpful! There are still a few ways I want to tweak the API from here, so I'll be working on that before I publish it as a beta. |
Beta Was this translation helpful? Give feedback.
-
I had posted above about some concerns for v6 moving to a more client-side management. However, I think I'm coming around to appreciate the changes here. About to go look at your v6 work, curious to see how much is still server-based vs client-side managed. |
Beta Was this translation helpful? Give feedback.
-
I'm reasonably happy with how things are shaping up so far, so I've started working on the new docs. So far I've written the introduction and quick-start pages. If anyone wants to get an early look, you can find them here: https://www.rvf-js.io/ |
Beta Was this translation helpful? Give feedback.
-
Good to see this.My biggest problem has been with validation and the current form element value. Also, it's unclear why value change / validation sometimes doesn't trigger even when you call setValue and validate. This occurs particularly in scenarios where the field is not a standard form element. It would be great to document how to trigger value change and validation correctly in these scenarios, bc sometimes it just seems impossible to achieve and it requires a full submit or a change on another field to finally trigger a change in state. |
Beta Was this translation helpful? Give feedback.
-
Edit: It seems to work fine with single fetch, but I couldn't tell you what is going wrong. I've tried both v5 and v6 and what works fine on v5 doesn't work on v6. I gave it a crack, but it seems like it doesn't work with a fetcher and single fetch. The server action is returning the validation errors, but the FormErrors is never set. |
Beta Was this translation helpful? Give feedback.
-
I've just noticed that you release the alpha version. I've checked documentation and it looks great. I have a question about Remix integration though. If we're supposed to use I will try to find some time to test this package. Thanks for working on it! |
Beta Was this translation helpful? Give feedback.
-
Hey @airjp73 whats your thoughts on how rvf deals with controlled fields the way that react-hook-form does? This being the field example from shadcn/ui
|
Beta Was this translation helpful? Give feedback.
-
Is there any working example using RemixJS? I tried using the example from the web, but it doesn't work - action function is not called. import {
json,
type ActionFunctionArgs,
type MetaFunction,
} from '@remix-run/node';
import { useActionData } from '@remix-run/react';
import { useForm, validationError } from '@rvf/remix';
import { withZod } from '@rvf/zod';
import { z } from 'zod';
export const meta: MetaFunction = () => {
return [
{ title: 'New Remix App' },
{ name: 'description', content: 'Welcome to Remix!' },
];
};
const validator = withZod(
z.object({
name: z.string().min(3),
email: z.string().email(),
password: z.string().min(8),
})
);
export async function action({ request }: ActionFunctionArgs) {
const data = await validator.validate(await request.formData());
if (data.error) {
console.log('error', data.error);
return validationError(data.error);
}
console.log('success', data.data);
return json(data.data);
}
export default function Index() {
const data = useActionData<typeof action>();
const form = useForm({
validator,
defaultValues: { name: '', email: '', password: '' },
});
return (
<form
className="flex flex-col gap-16 max-w-lg p-16"
{...form.getFormProps()}
>
<div className="flex flex-col gap-1.5">
<label htmlFor="name">Name</label>
<input
className="py-2 px-4 h-10 text-sm border border-gray-300 text-gray-900 rounded-md"
id="name"
{...form.getInputProps('name')}
/>
{form.error('name') && (
<p className="text-red-500">{form.error('name')}</p>
)}
</div>
<div className="flex flex-col gap-1.5">
<label htmlFor="email">Email</label>
<input
className="py-2 px-4 h-10 text-sm border border-gray-300 text-gray-900 rounded-md"
id="name"
{...form.getInputProps('email')}
/>
{form.error('email') && (
<p className="text-red-500">{form.error('email')}</p>
)}
</div>
<div className="flex flex-col gap-1.5">
<label htmlFor="password">Password</label>
<input
className="py-2 px-4 h-10 text-sm border border-gray-300 text-gray-900 rounded-md"
id="name"
{...form.getInputProps('password')}
/>
{form.error('password') && (
<p className="text-red-500">{form.error('password')}</p>
)}
</div>
<div className="p-4 bg-gray-100 text-gray-600 text-sm rounded-md">
{JSON.stringify(data)}
</div>
<button
type="submit"
className="px-3 py-2 bg-gray-300 text-gray-900 rounded-md"
>
Submit
</button>
</form>
);
} |
Beta Was this translation helpful? Give feedback.
-
@airjp73 Is it possible to export MinimalInputProps type to be able to do this hight order component? Or is there a better solution? export type FormFieldProps<T extends string> = {
scope: FormScope<ValueOfInputType<T>>;
children?: (
props: MinimalInputProps,
error: string | null
) => React.ReactNode;
}; I use the following logic using react-hook-form and it would be good to be able to do something similar. <FormField control={control} name="user.email">
{(props, ref, error) => (
<TextField {...props} isDisabled>
<Label>{t("Email")}</Label>
<Input type="email" ref={ref} inputMode="email" />
{error && <ErrorMessage>{error}</ErrorMessage>}
</TextField>
)}
</FormField> |
Beta Was this translation helpful? Give feedback.
-
Has anyone done a multi-step with RVF and Remix? @airjp73 would this use one form and scope? or multiple forms with sub actions? I had been thinking the 3 forms with sub actions but wondering how would scope work in this scenario? |
Beta Was this translation helpful? Give feedback.
-
@airjp73 have you thought of adding a `form.array('locations').values("elementInsideLocationsArray") So if you had locations as
You could essentially get the sum of all quantity values... |
Beta Was this translation helpful? Give feedback.
-
@airjp73 Is this a bug or am i doing something wrong? Types are not available in handleSubmit function.
|
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
rvf has been so refreshing! I'm having an issue trying to type reusable components to accept a form that satisfies some FormInputData. Currently I'm using |
Beta Was this translation helpful? Give feedback.
-
It sounds like you're doing a complete rewrite. I truly hope that working with useControlledField will become less of a pain and some of the issues we've had processing updates to the form state will be resolved. I would also strongly suggest that you use the new useForm hooke and keep the old ValidatedForm component as a backward compatible, possibly deprecated, wrapper around the new hook architecture. Otherwise migrating will require rewriting all of our code or writing wrappers ourselves, and that would be like React moving to hooks and saying "oh yeah, all your class based component won't work anymore". Maybe a |
Beta Was this translation helpful? Give feedback.
-
The first draft of the migration guide is up! https://www.rvf-js.io/migration If you notice anything that's missing or would be helpful to get clarification on (in this doc specifically), please feel free to reply to this comment with feedback. |
Beta Was this translation helpful? Give feedback.
-
The docs should cover everything now and I think everything is pretty stable at this point. So I'm going to say that RVF is now in Beta instead of Alpha. Now I'm going to start putting this into production and hopefully catch any lingering issues. |
Beta Was this translation helpful? Give feedback.
-
I've migrated quite a bit of my production app to use this now and there have been no major issues. So I'm officially releasing v6 today 🥳 |
Beta Was this translation helpful? Give feedback.
-
Released: Version 6 of RVF
I've been working on a ground-up rewrite of RVF for a bit, that I'm planning to release as version 6. I wanted to open this up to some discussion and feedback if anyone is interested, because there are some large-ish changes. I think these changes will address many of the issues currently open, and permanently address issues that have come up muliple times in the past.
Links to some issues (non-exhaustive) that will be fixed.
useField
vsuseFieldArray
#360useControlField
isundefined
on first render #351isValid
without displaying error messages. #307defaultValues
are flattened key paths. #245Note: This is an early version of the API and I'll be making changes to this document as things change.
Plain-react core with Remix adapter
I'm still not happy with the current landscape of form libraries for plain React, and I think some of the ideas of this library, plus the new ideas I have now, will make a good plain-React form library.
A big advantage of this approach, for me, is that I'll be able to spend more time maintiaining it. One big reason I haven't been able to dedicate much time to adding new features to RVF lately is that my job doesn't use Remix (currently using Next). I'm planning to use the plain-react version of this library in my professional work, which will give me more time to dedicate to it.
Another advantage I see for this approach is the changing landscape of forms in React in general. With all the form-related changes in React 19, we may even be able to get many of the Remixy things we like from this library to work with plain React.
Renaming the library to just RVF
Since the core won't be remix-specific, I think it makes sense to rename to just RVF. Specifically, the packages will be under the
@rvf
namespace.@rvf/react
@rvf/remix
@rvf/zod
@rvf/yup
Adding a
useForm
hookHaving the ability to use a hook to create your form provides a lot of composability and flexibility. It also gives much more potential for full type-safety. Additionally, this enables at least one cool idea I haven't really seen in other libraries (more on that later).
I'm still planning on keeping the
ValidatedForm
component around, at least for theremix
adapter, but I think the docs for newcomers will start withuseForm
as the default.Removing "use outside of form" features
This feature was originally intended as a way to try to bring the library closer to the way HTML forms work. You can specify a
form
prop on a nativeinput
component to attach it to a form that it isn't a descendant of. But over time, this feature kinda mophed into the main way of accessing form context and other helpers in the parent component of theForm
. This inherently comes with a lot of problems/tradeoffs.Now that we'll have a
useForm
hook, all the use-cases for the "use outside of form" features can be fully satisfied by using theuseForm
hook instead of theValidatedForm
component. This change will be called out in a remix-specific migration guide.Reworking internal state management to behave more predictably and consistently
As well as potentially reworking
useControlField
.One decision I made early on was to not track any field values in state. Then once we added
useControlField
anduseFieldArray
, those necessarily need to use state to some extent. The fact that controlled field values are tracked in state, whileuseField
values are not has resulted in confusing and inconsistent behavior in some cases.To alleviate this, we'll track all form values in state, and make it possible to update the values of both controlled and uncontrolled fields with a unified API. Ultimately, the FormData from the form will still be the source of truth for what gets validated and submitted.
However, now we'll also have another lever to pull. If you want (mostly if you're using plain react), you'll be able to validate and use the values in state, rather than the FormData itself. The API for this is still up in the air.
Additionally, since all of this will narrow the use cases for useControlField to only custom components and not just updating form values, I have ideas for a more specific helper to cater to this use case.
Supporting setting uncontrolled input values via a
ref
getInputProps
will now return aref
that will be used to support helpers likesetValue
andresetField
in uncontrolled components. This eliminates a whole class of use-cases foruseControlField
.Additionally,
useField
will also have agetControlProps
helper to support integrating controlled components. That will also return aref
that will be used to set focus to the field when the form focuses the first invalid field in the form.Opt-in "state mode"
The default behavior of this library is still to look at the DOM itself when performing any validation or submission. Since
remix
(and soon the React 19 form action) use data directly from the form element, this will likely be the mode most people want to use most of the time.But sometimes you really need more control than that. So in v6 there will be a
submitMode: "state"
option. In state mode, instead of using the FormData from the form element itself, validation and submission will run against the form values as they're tracked in the libraries internal state management the same way more traditional form libraries do it. This means that it will be possible to include data in your submission for fields that are no longer in the DOM.A drawback of using this is that you no longer support progressive enhancement and your form must use JS.
Unifying and simplifying the API
We have several different hooks right now for accessing form state. Part of this is because we don't want components to render on every change to the form state, just the ones they actually want to subscribe to.
These days, proxy-based state access tracking is very common, and it's something I've used for the rewrite so far. The remix adapter will still have some of the old hooks around to ease migration burden, but they will be deprecated in favor of the APIs.
Migration considerations
Most of the old APIs will exist in some form in the new version, though some (like useControlField) will be deprecated and only provided to ease migration burden.
Additionally, since this will be a new package rather than a version bump of
remix-validated-form
, it should be possible to use both versions at once if your project is large and the migration needs to be done over time.New stuff
The base API
What you see here is still in flux and may totally change before I'm finishing.
With the new API, it will be possible to write simple forms with only a top-level hook and without direct field-level integration. Of course, the existing context-based style will still be present, as well as a new way of cleaning up your abstractions (scoping api).
Scoping API for reusable components
It will be easy to create re-usable components for the form that are still typesafe using
form.scope
.The object returned by
form.scope
cannot, iteself, be used to subscribe to form changes, and must be interpreted by auseForm
call.This API could also be used to create re-usable field components if you want.
Context API
If you still want to use a context-based API, that will still be available.
Removed APIs
Some APIs will be removed and I'm going to keep track of them here as I remove them.
useAwaitValue
- undocumented, but still public. Won't be necessary at all.handleReceiveFocus
option - The replacement for this option will be to provide aref
to the element that needs to be focused.Progress tracking & timeline
I think the timeline for this whole project is somewhere between 2 weeks and 2 months, depending on how much time I have outside my job to work on it.
Beta Was this translation helpful? Give feedback.
All reactions