Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/airjp73/rvf
Easy form validation and state management for React and Remix
https://github.com/airjp73/rvf
Last synced: 2 days ago
JSON representation
Easy form validation and state management for React and Remix
- Host: GitHub
- URL: https://github.com/airjp73/rvf
- Owner: airjp73
- License: mit
- Created: 2021-11-24T05:22:38.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2024-08-09T16:05:26.000Z (3 months ago)
- Last Synced: 2024-08-09T17:49:49.028Z (3 months ago)
- Language: TypeScript
- Homepage: https://rvf-js.io
- Size: 5.3 MB
- Stars: 799
- Watchers: 5
- Forks: 61
- Open Issues: 7
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-remix - Form component and utils for easy form validation in remix
- awesome-remix - Form component and utils for easy form validation in remix
README
# RVF
(Formerly Remix Validated Form)
Easy form validation and state management for React.
### Progressively enhanced
RVF leans into native form APIs, so it's easy to add to your existing forms.
It even works without JavaScript on the client if you're using a server-rendered framework like [Remix](https://remix.run).### Powerful
When you need to scale up and write bigger, more complicated forms, RVF can scale with you.
- Easily manages nested objects and arrays in at typesafe way -- even when they're recursive.
- Set default values for the whole form in one place.
- Re-use your validation on the server.## Docs
The docs are located a [rvf-js.io](https://rvf-js.io).
## Simple example
> [!Note]
> See this example in action on the [documention site](https://rvf-js.io).Plain React
```tsx
import { useForm } from "@rvf/react";
import { withZod } from "@rvf/zod";
import { z } from "zod";
import { MyInput } from "~/fields/MyInput";
import { Button } from "~/ui/button";
import { createProject } from "./api";
import { ErrorMessage } from "~/fields/ErrorMessage";
import { showToastMessage } from "~/lib/utils";
import { EmptyState } from "~/ui/empty-state";const validator = withZod(
z.object({
projectName: z
.string()
.min(1, "Projects need a name.")
.max(50, "Must be 50 characters or less."),
tasks: z
.array(
z.object({
title: z
.string()
.min(1, "Tasks need a title.")
.max(50, "Must be 50 characters or less."),
daysToComplete: z.coerce.number({
required_error: "This is required",
}),
}),
)
.min(1, "Needs at least one task.")
.default([]),
}),
);export const ReactExample = () => {
const form = useForm({
validator,
defaultValues: {
projectName: "",
tasks: [] as Array<{ title: string; daysToComplete: number }>,
file: "" as File | "",
},
handleSubmit: async ({ projectName, tasks }) => {
await createProject({ name: projectName, tasks });
return projectName;
},
onSubmitSuccess: (projectName) => {
showToastMessage(`Project ${projectName} created!`);
form.resetForm();
},
});return (
Tasks
{form.error("tasks") && (
{form.error("tasks")}
)}
{form.array("tasks").map((key, item, index) => (
form.array("tasks").remove(index)}
>
Delete
))}
{form.array("tasks").length() === 0 && (
No tasks yet
)}
{
const nextTaskIndex = form.array("tasks").length();
await form.array("tasks").push({
daysToComplete: 0,
title: "",
});
form.focus(`tasks[${nextTaskIndex}].title`);
}}
>
Add task
Submit
);
};
```Remix
```tsx
import {
isValidationErrorResponse,
useForm,
validationError,
} from "@rvf/remix";
import { withZod } from "@rvf/zod";
import { z } from "zod";
import { MyInput } from "~/fields/MyInput";
import { Button } from "~/ui/button";
import { createProject } from "./api";
import { ErrorMessage } from "~/fields/ErrorMessage";
import { showToastMessage } from "~/lib/utils";
import { EmptyState } from "~/ui/empty-state";
import { json, useActionData } from "@remix-run/react";
import { ActionFunctionArgs } from "@remix-run/node";const validator = withZod(
z.object({
projectName: z
.string()
.min(1, "Projects need a name.")
.max(50, "Must be 50 characters or less."),
tasks: z
.array(
z.object({
title: z
.string()
.min(1, "Tasks need a title.")
.max(50, "Must be 50 characters or less."),
daysToComplete: z.coerce.number({
required_error: "This is required",
}),
}),
)
.min(1, "Needs at least one task.")
.default([]),
}),
);export const action = async ({ request }: ActionFunctionArgs) => {
const data = await validator.validate(await request.formData());
if (data.error) return validationError(data.error);const { projectName, tasks } = data.data;
await createProject({ name: projectName, tasks });
return json({ projectName });
};export const ReactExample = () => {
const data = useActionData();
const form = useForm({
validator,
defaultValues: {
projectName: "",
tasks: [] as Array<{ title: string; daysToComplete: number }>,
},
onSubmitSuccess: () => {
// We know this isn't an error in the success callback, but Typescript doesn't
if (isValidationErrorResponse(data)) return;// This isn't always the best way to show a toast in remix.
// https://www.jacobparis.com/content/remix-form-toast
showToastMessage(`Project ${data?.projectName} created!`);
form.resetForm();
},
});return (
Tasks
{form.error("tasks") && (
{form.error("tasks")}
)}
{form.array("tasks").map((key, item, index) => (
form.array("tasks").remove(index)}
>
Delete
))}
{form.array("tasks").length() === 0 && (
No tasks yet
)}
{
const nextTaskIndex = form.array("tasks").length();
await form.array("tasks").push({
daysToComplete: 0,
title: "",
});
form.focus(`tasks[${nextTaskIndex}].title`);
}}
>
Add task
Submit
);
};
```## Getting started
### Install
RVF can be used with any flavor of React, but there's also an adapter specifically for [Remix](https://remix.run).
- @rvf/remix
- @rvf/reactFor Remix users:
```bash
npm install @rvf/remix
```For plain React or other frameworks like Next.js:
```bash
npm install @rvf/react
```#### Validation library adapter
There are official adapters available for `zod` and `yup`.
If you're using a different library, see the [Validation library support](http://rvf-js.io/validation-library-support) seciton of the docs..- @rvf/zod
- @rvf/yupFor Zod users:
```bash
npm install @rvf/zod
```For Yup users:
```bash
npm install @rvf/yup
```## Further reading
For more details on how to use RVF, check out the [documentation site](https://rvf-js.io).