AutoForm Documentation

repository·main·Indexed 25 days ago

https://github.com/vantezzen/autoform

A library that automatically renders forms based on data schemas such as Zod, Yup, or Joi. AutoForm is UI-library and schema-agnostic, providing official integrations for Material UI, shadcn/ui, Mantine, Ant Design, and Chakra, as well as support for form engines like react-hook-form and tanstack-form.

Tokens
45.9K
Snippets
139
Records
233
Agent score
85%

What's inside AutoForm

  1. Determine when to use AutoForm

    main

    AutoForm is best suited as a drop-in form builder for internal tools and simple forms that rely on existing schemas (e.g., creating an admin panel to edit user profiles based on an API schema).

    Key Characteristics:

    • Automatic Mapping: It maps schema fields to input components, connects them to your form library, and sets up validation.
    • Customization: It provides the fieldConfig option for rendering customization and escape hatches for deeper customization.
    • Limitations: It is not intended to be a full-featured form builder for every possible schema edge case. For highly complex or multi-page forms, you may need to customize the renderer or use external solutions like AutoForm YAML.
  2. Understand AutoForm Architecture

    main

    AutoForm is a four-layer system. To avoid import errors, follow these layer boundaries:

    1. Core Layer (@autoform/core): Contains types and utilities. Rarely imported directly.
    2. Schema Provider Layer (@autoform/zod, @autoform/yup, or @autoform/joi): Handles parsing, validation, and fieldConfig.
    3. React Adapter Layer (@autoform/react): Provides shared React contracts and implementations for React Hook Form or TanStack Form.
    4. UI Library Layer (@autoform/mui, @autoform/mantine, @autoform/ant, @autoform/chakra, or shadcn/ui): Provides pre-wired UI components.

    Critical Import Rules

    • AutoForm Component: Import from the specific UI adapter path (e.g., @autoform/mui/react-hook-form or @autoform/mui/tanstack-form). For shadcn, use your local path: @/components/ui/autoform/react-hook-form.
    • Schema Providers: Import the provider and fieldConfig from the matching schema package (e.g., ZodProvider from @autoform/zod).
    • Shared Types: Import AutoFormFieldProps, FieldWrapperProps, etc., from @autoform/react.
    • Custom Field Binding: Use useController from react-hook-form OR useFieldContext from @autoform/react/tanstack-form. Do not mix them.
  3. Understand the AutoForm modular architecture

    main

    AutoForm is structured in four distinct layers to allow flexibility between schema libraries and UI frameworks:

    1. @autoform/core: The foundation. It is agnostic to schemas and UI, providing core types, interfaces, utility functions for parsing/validation, and a dependency manager for form fields.
    2. Schema Providers (e.g., @autoform/zod): Adapts specific schema libraries (Zod, Yup, Joi) to AutoForm by implementing the SchemaProvider interface. They handle schema parsing, validation logic, and default values.
    3. @autoform/react: Provides React context, contracts, and adapter-agnostic hooks. It exposes specific runtime implementations via subpaths: @autoform/react/react-hook-form and @autoform/react/tanstack-form.
    4. UI-specific libraries (e.g., @autoform/mui): The final layer that provides themed components (inputs, selects, etc.) and wraps the selected @autoform/react adapter path.

    Typical Workflow: Define schema $\rightarrow$ Pass to Schema Provider $\rightarrow$ Use UI-specific AutoForm component $\rightarrow$ Core parses schema and generates fields $\rightarrow$ UI components render fields and handle input $\rightarrow$ Validated data is passed to onSubmit.

  4. Access form data and state inside AutoForm

    main

    Because AutoForm wraps the form with React Hook Form's FormProvider, components rendered as children of <AutoForm> can access form methods and state using useFormContext from react-hook-form without needing to pass a control object manually.

    Data/State Read Methods Reference

    MethodUse when
    useWatchSubscribe to input changes with isolated component re-renders.
    watchSubscribe to input value changes and rerender the calling component.
    formStateRead real-time form state properties; rerenders the calling component.
    subscribeSubscribe to form state changes outside the render cycle; no rerender.
    getValuesRead current form values without subscribing to rerenders.
    import { useFormContext } from "react-hook-form";
    
    function QuickActions() {
      const {
        watch,
        setValue,
        reset,
        formState: { isValid },
      } = useFormContext();
      // ...
    }
  5. Customize the Submit Button

    main

    There are three ways to handle the submit button:

    1. Default Button: Use the withSubmit prop to render the default button.
    2. Custom Child: Pass a custom button as a child of <AutoForm>.
    3. External Button: If the button is outside the <AutoForm> element, use formProps.id to link it via the HTML form attribute.
    // 1. Default
    <AutoForm schema={schemaProvider} onSubmit={handleSubmit} withSubmit />
    
    // 2. Custom Child
    <AutoForm schema={schemaProvider} onSubmit={handleSubmit}>
      <button type="submit">Create Account</button>
    </AutoForm>
    
    // 3. External
    <AutoForm
      schema={schemaProvider}
      onSubmit={handleSubmit}
      formProps={{ id: "my-form" }}
    />
    <button type="submit" form="my-form">
      Submit
    </button>
  6. Submit an AutoForm

    main

    You can either use the built-in submit button by adding the withSubmit prop, or provide your own custom submit button as a child of the AutoForm component.

    // Using default submit button
    <AutoForm schema={schemaProvider} onSubmit={handleSubmit} withSubmit />
    
    // Using a custom submit button
    <AutoForm schema={schemaProvider} onSubmit={handleSubmit}>
      <button type="submit">Save</button>
    </AutoForm>
  7. Add custom field types to AutoForm

    main

    You can extend AutoForm by adding custom field components.

    1. Create your component using AutoFormFieldProps and useController from react-hook-form.
    2. Add the component to the formComponents object in your AutoForm.tsx file, using a key that matches the fieldType you intend to use in your schema.

    Example:

    // In src/AutoForm.tsx
    const formComponents: CustomAutoFormFieldComponents = {
      string: StringField,
      number: NumberField,
      date: DateField,
      custom: CustomField, // 'custom' is the fieldType used in the schema
    };
  8. Common AutoForm usage patterns

    main

    AutoForm supports several advanced form patterns. Depending on your chosen form engine (React Hook Form or TanStack Form), you can implement:

    • Real-time validation: Live errors, valid form state, and conditional submit button enabling.
    • Dialog submit/reset: Handling form submission and resetting from buttons located outside the form component.
    • Custom fields: Implementing sliders, color pickers, date pickers, file uploads, or radio cards. For components that must own the field value, use fieldConfig({ fieldType }) combined with formComponents instead of simple wrappers.
    • Dependent/conditional fields: Cascading selects, showing/disabling/resetting UI based on other field values (watching fields).
    • Multi-step forms: Wizards using multiple schemas, step-wise validation, and collecting data across steps.
    • Nested AutoForm: Rendering a second AutoForm inside a custom field (e.g., for object editors or subforms in dialogs) and writing its submitted value to the parent field.
    • Dynamic schema playground: Parsing schema strings (e.g., from a Monaco editor) to render AutoForm dynamically.
  9. Create an interactive, dynamic form builder

    main

    You can build a dynamic form builder by combining a code editor (like Monaco) with AutoForm:

    1. Capture the Zod schema as a string from the editor.
    2. Evaluate the schema string on every editor change.
    3. Pass the resulting ZodProvider to the AutoForm component.

    Warning: This pattern typically requires eval() to parse the editor input. Avoid using eval() with untrusted input or in production server-side code.

  10. Control AutoForm from external components

    main

    If you need to trigger submit, reset, or setFieldValue from components located outside the <AutoForm> tree (e.g., a Dialog footer), use the useAppForm hook.

    Important:

    • Pass the result of useAppForm to the formControl prop of AutoForm.
    • Keep the options passed to useAppForm stable (use formOptions or useMemo) to avoid issues.
    • Pass initial values via the defaultValues prop on AutoForm, not inside the formOptions object.
    import * as React from "react";
    import { formOptions } from "@tanstack/react-form";
    import { useAppForm } from "@autoform/react/tanstack-form";
    
    // Keep options stable
    const staticOptions = formOptions({});
    
    function MyForm() {
      const form = useAppForm(staticOptions);
    
      return (
        <>
          <AutoForm
            schema={schemaProvider}
            formControl={form}
            defaultValues={{ username: "" }}
          />
          <button type="button" onClick={() => void form.handleSubmit()}>
            Submit
          </button>
        </>
      );
    }