RVF (Remix Validated Form)

repository·main·Indexed 21 days ago

https://github.com/airjp73/rvf

A library for form validation and state management in React, designed for progressive enhancement and compatibility with native form APIs. It supports server-side rendering frameworks like Remix and Next.js through specialized adapters such as @rvf/react-router and @rvf/react, and provides schema validation integration via @rvf/zod, @rvf/yup, and @rvf/valibot.

Tokens
48.4K
Snippets
156
Records
207
Agent score
75%

What's inside RVF

  1. Use the RVF Zod adapter

    main
    The @rvf/zod package is an adapter that allows you to use Zod schemas for form validation within the RVF (Remix Validated Form) ecosystem. This enables seamless integration between Zod's schema validation and RVF's progressive enhancement and form management capabilities.
  2. Use RVF set-get for deeply nested data

    main
    The @rvf/set-get package provides internal utilities and types designed for working with deeply nested data structures. While primarily used by the RVF ecosystem, the API is considered stable and can be used in external projects for managing nested object access and updates.
  3. Common traits of RVF input types

    main

    RVF validates and submits data directly from the HTML form element by default, supporting all native input types.

    Types

    Empty inputs are generally represented as null in cases where the main type of the input is not a string.

    Setting default values

    • All input types (except file) can have their default value set using a string.
    • For non-string types (like number), you can typically set the default value using that specific type.

    Observing and setting values

    The type returned by form.value(fieldName) is always the same as the type passed into defaultValues. You should use the same type when calling form.setValue(fieldName, value).

    Validating

    Unless you are using state mode, the data received by your schema will always be a string or a string[]:

    • If only one input exists for a field, the value is a string.
    • If multiple inputs share the same name, the value is a string[].
  4. Type safety for defaultValues

    main

    When using a modern Standard Schema validator, the type of defaultValues is automatically inferred from your schema. The resulting form object methods (such as form.value() and form.setValue()) will use this inferred input type.

    Note: If you are using a legacy validator, the type of defaultValues is not inferred from the validator; instead, it is inferred solely from the defaultValues object itself.

    const form = useForm({
      schema: z.object({
        name: z.string(),
      }),
      // Type inferred from schema above!
      defaultValues: {
        name: "John Doe",
      },
    });
  5. Use `FieldApi` to interact with form fields

    main

    The FieldApi object provides a set of helper methods and properties to manage the state, validation, and DOM properties of a specific field within an RVF form. It allows you to handle values, errors, touched states, and DOM attributes like ref and name easily.

    // Example of accessing FieldApi properties
    console.log(field.name);
    console.log(field.value);
    console.log(field.error);
    console.log(field.touched);
  6. Use `FieldArrayApi` to manage field arrays

    main

    The FieldArrayApi provides a set of helper methods for interacting with and manipulating arrays of fields within a form. It allows you to add, remove, move, and iterate over items in an array while providing scoped FormApi instances for each individual item.

    // Example of using the map method to render array items
    myArray.map((key, item, index) => (
      <div key={key}>
        {item.value("name")}
        <button
          type="button"
          onClick={() => {
            myArray.remove(index);
          }}
        >
          Delete
        </button>
      </div>
    ));
  7. Handle nested objects using dot notation

    main

    RVF handles nested object structures by treating field names as paths using standard JavaScript dot notation. When naming your <input /> elements, use the name attribute to specify the path to the property within the resulting data object.

    const inputs = (
      <>
        <input name="todo.title" />
        <input name="todo.description" />
      </>
    )
    
    // The resulting data object will look like:
    const result = {
      todo: {
        title: "Take out the trash",
        description: "I should really do this",
      },
    }
  8. Use `scope` to create subforms and nested abstractions

    main

    RVF allows you to scope a form down to a specific part of your data structure using the scope method on the FormApi object. This is useful for creating reusable components that represent subforms or individual fields without needing to know the full shape of the parent form.

    When you call scope(fieldName), it returns a FormScope object. You can chain multiple scope calls to navigate deeply nested or recursive data structures.

    To use a FormScope in a component, you must pass it to one of the following hooks:

    • useFormScope
    • useField
    • useFieldArray
    const form = useForm({
      defaultValues: {
        foo: "foo",
        bar: { baz: "baz" }
      },
      // ...etc
    });
    
    // Scoping to a top-level field
    const fooScope = form.scope("foo");
    // fooScope type: FormScope<string>
    
    // Scoping to a nested field
    const barScope = form.scope("bar");
    // barScope type: FormScope<{ baz: string }>
    
    // Chaining scope for deep nesting
    const bazScope = barScope.scope("baz");
    // bazScope type: FormScope<string>