VitNode
Forms

Auto Form

Automatically generate form fields based on a schema.

Support Fields

Installation

pnpm i react-hook-form zod

Usage

import { z } from 'zod';
import { AutoForm } from 'vitnode-frontend/form/auto-form';
 
// for input
import { AutoFormInput } from 'vitnode-frontend/form/fields/input';
const formSchema = z.object({
  username: z.string(),
});
<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: 'username',
      component: AutoFormInput,
    },
  ]}
/>

Component Props

import {
  AutoFormInput
} from "vitnode-frontend/components/auto-form/fields/input";
 
<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: "username",
      component: props => <AutoFormInput {...props} type="password">, // [!code highlight]
    }
  ]}
/>;

Zod Configuration

Validators

Our component is smart and well integrated with Zod, so you can use all the validators that Zod provides. For exampele if you use zod.string().min(8), the input will automatically have a minlength="8" attribute.

Validation methods not supported by HTML will automatically be checked when the form is submitted.

Descriptions

You can add a description to your field by passing the description prop.

<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: 'username',
      component: AutoFormInput,
      description: 'Test description', 
    },
  ]}
/>

Optional Fields

By default all fields are required. You can make a field optional by using the optional method.

const formSchema = z.object({
  username: z.string().optional(),
});

Default Values

You can set a default value for a field by using the default method.

const formSchema = z.object({
  username: z.string().default('test'),
});

Label

You can set a label for a field by using the label method.

<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: 'username',
      component: AutoFormInput,
      label: 'Your username', 
    },
  ]}
/>

Submit Form

You can use the onSubmit prop to get the form data.

<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: 'username',
      component: AutoFormInput,
    },
  ]}
  onSubmit={data => {
    console.log(data);
  }}
/>

You can get access to the form from react-hook-form by using the form prop as second argument.

<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: 'username',
      component: AutoFormInput,
    },
  ]}
  onSubmit={(data, form) => {
    console.log(data);
    form.setError('username', { type: 'manual', message: 'This is an error' });
  }}
/>

You can also move onSubmit to a separate function.

const onSubmit = async (
  values: z.infer<typeof formSchema>,
  form: UseFormReturn<z.infer<typeof formSchema>>,
) => {};

Handle Errors

You can handle errors by using the Sonner component.

import { toast } from 'sonner';
 
const onSubmit = async (
  values: z.infer<typeof formSchema>,
  form: UseFormReturn<z.infer<typeof formSchema>>,
) => {
  try {
    await mutationApi(values);
 
    toast.success(t('success.title'), {
      description: t('success.description'),
    });
  } catch (_) {
    toast.error(t('errors.title'), {
      description: t('errors.internal_server_error'),
    });
  }
};

Submit Button

Component has a submit button by default with the text Save include translation for this text. You can change the component by using the submitButton prop.

<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: 'username',
      component: AutoFormInput,
    },
  ]}
  submitButton={props => (
    <Button {...props} className="w-full">
      {t('sign_in.form.submit')}
    </Button>
  )}
/>

Remember to pass the props to the button to keep the submit functionality.

Theme

You can change the theme of the form by using the theme prop and passing horizontal or vertical.

<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: 'username',
      component: AutoFormInput,
    },
  ]}
  theme="horizontal"
/>

Get Values from Form

You can get the values from the form by using the onValuesChange prop.

const [values, setValues] = useState<Partial<z.infer<typeof formSchema>>>({});
 
<AutoForm onValuesChange={setValues} />;

Dependencies

AutoForm allow to create dependencies between fields. For example allowing one field's visibility or behavior to be controlled based on the value of another. Pass dependencies prop with an array of objects.

import { DependencyType } from 'vitnode-frontend/form/type';

Hides

For example if provider is not smtp, the field smtp will be hidden.

<AutoForm
  formSchema={formSchema}
  fields={[]}
  dependencies={[
    {
      sourceField: 'provider',
      type: DependencyType.HIDES, 
      targetField: 'smtp',
      when: (provider: string) => provider !== 'smtp',
    },
  ]}
/>

Requires

For example if provider is resend, the field resend_key will be required.

<AutoForm
  formSchema={formSchema}
  fields={[]}
  dependencies={[
    {
      sourceField: 'email.provider',
      type: DependencyType.REQUIRES, 
      targetField: 'resend_key',
      when: (provider: string) => provider === 'resend',
    },
  ]}
/>

This dependencie will hide only the Optional text. That's mean you should change your formSchema to make the field required.

For example using refine method from Zod.

const formSchema = z
  .object({
    provider: z.nativeEnum(EmailProvider).default(data.provider),
    smtp: z.object({
      host: z.string().default(data.smtp_host ?? ''),
    }),
    resend_key: z.string().default(''),
  })
  .refine(input => {
    if (input.provider === 'smtp') {
      return input.smtp.host !== '';
    } else if (input.provider === 'resend') {
      return input.resend_key !== '';
    }
 
    return true;
  });

Disables

For example if provider is not smtp, the field smtp will be disabled.

<AutoForm
  formSchema={formSchema}
  fields={[]}
  dependencies={[
    {
      sourceField: 'email.provider',
      type: DependencyType.DISABLES, 
      targetField: 'smtp',
      when: (provider: string) => provider !== 'smtp',
    },
  ]}
/>

Set options

For example if vegetarian checkbox hides the Beef Wellington option from mealOptions if its not already selected.

<AutoForm
  formSchema={formSchema}
  fields={[]}
  dependencies={[
    {
      sourceField: 'vegetarian',
      type: DependencyType.SETS_OPTIONS, 
      targetField: 'mealOptions',
      when: (vegetarian, mealOption) =>
        vegetarian && mealOption !== 'Beef Wellington',
      options: ['Pasta', 'Salad'],
    },
  ]}
/>

On this page