Skip to content

Props and Event

KeyTypeDefaultDescription
valueFormValue<Form>{}Object value that you want to edit.
formForm[]Form structure with validation config.
disabledbooleanfalseProp to disable all fields.
mode'input''show''input'
Event
onChangeFormValue<Form>{}Return the new object value.
onErrorbooleanfalseDefine if form has error.

Form prop

Form

A form is a list of Section with a list of Fields inside.

type Form = Section[]
[
  {
    // Form already open without collapse
    fields: [
      // ...
    ]
  },
  {
    name: 'Section name',
    header: {
      label: 'Section label',
      open: true
    },
    fields: [
      // ...
    ]
  }
  // ...
]

Section

Required parameters:

  • fields: Fields section contains a list of row and column with field inside. All details are described below.

Optional parameters:

  • name: Section name, if you put section name here, it will appear as a collapsed section.
  • header: Section header, you can use it to set the label of the section and if it is open or not. If present the section is collapsable. You can specify open true/false to set the section open or closed by default.
  • header.label: Label of the section header. If not set, it will be generated automatically from the name.
  • header.open: If true, the section will be open by default.

You can check the interface below:

type SectionHeader = {
  label?: string
  collapsable?: {
    open?: boolean
  }
}
type Section = {
  header?: SectionHeader
  fields: Fields
}

Fields

Fields is a list of rows with a list of column inside. In every column you can put a field.

type Fields = Field[][]

Eg: If you want to create a form with 2 fields in the first row and 1 field in the second row you can do:

  fields: [
    [
      {
        // FIELD
      },
      {
        // FIELD
      }
    ],
    [
      {
        // FIELD
      }
    ],
  ]
}

Field

Field must have a type and a name and others optional parameters described below:

Required parameters:

  • type: Type of field. You can use one of the type described in the interface below or implement your own custom component and pass the instance of it.
  • name: Name of the field. It will be used to get the value of the field. This field supports dot syntax, so you can use it to create nested fields like user.name or user.address.street.

Optional parameters:

  • className: Class name of the field. It will be used to set the class name of the field, you can use it to add custom styles or classes.
  • size: Size of the field. You can use one of the size described in the interface below to set the size of the field.
  • label: Label of the field. It will be used to override the label of the field, if not set the label will be generated automatically from the name.
  • caption: Caption of the field. It will be used to show a caption under the field if needed.
  • disabled: If true, the field will be disabled.
  • visible: If true, the field will be visible. If false, the field will be hidden.

Custom parameters:

  • options: Options for the field. It will be used to set the options of the field.
  • validation: Validation rules for the field. It will be used to validate the field value through Zod schema.
  • render: Custom render function for the field. It will be used to render the field if you use a show mode.
  • private: If the callback is set, it will be used to render the field in a private mode, meaning that the field will not be visible in the form but can be used to store data.

Every type has its own parameters, basically they can have an options, validation, render and private.

Options

Options are used to set the options of the field, like items for a select or options for a checkbox. The options are different for every type of field, you can check the interface below for more details.

Validation

Validation is handled via Zod schema. Below you can find practical examples for each common Zod schema type, both simple and advanced usage.

Validation could be a zod rule or a function that returns a zod rule. The function will be called with the form value and should return a zod rule.

In this way you can create dynamic validation rules based on the form value.

[
  {
    type: 'number',
    name: 'min',
  },
  {
    type: 'text',
    name: 'name',
    validation: z.number(),
    // or
    validation: (v) => {
      const min = v?.min ? (v.min as number) : 0
      return z.number().int().min(min).max(99)
    }
  }
]
ZodString
// simple
z.string()
// advanced
z.string().min(5).max(20).email({ message: 'Invalid email' })
ZodNumber
// simple
z.number()
// advanced
z.number().int().min(0).max(100)
ZodBoolean
// simple
z.boolean()
// advanced
z.boolean().refine(val => val === true, { message: 'Must be true' })
ZodDate
// simple
z.date()
// advanced
z.date().min(new Date('2024-01-01'), { message: 'Date too old' })
ZodArray
// simple
z.array(z.string())
// advanced
z.array(z.number().min(1)).min(2, { message: 'At least 2 numbers' })
ZodObject
// simple
z.object({ name: z.string(), age: z.number() })
// advanced
z.object({
  user: z.object({
    email: z.string().email(),
    roles: z.array(z.string())
  })
})
ZodEnum
// simple
z.enum(['A', 'B', 'C'])
// advanced
z.enum(['admin', 'user', 'guest'])
ZodLiteral
// simple
z.literal('ok')
// advanced
z.union([z.literal('yes'), z.literal('no')])
ZodNull
// simple
z.null()
// advanced
z.union([z.string(), z.null()])
ZodUndefined
// simple
z.undefined()
// advanced
z.union([z.string(), z.undefined()])
ZodAny
// simple
z.any()
// advanced
z.any().refine(val => typeof val === 'object', { message: 'Must be an object' })
ZodUnknown
// simple
z.unknown()
// advanced
z.unknown().refine(val => Array.isArray(val), { message: 'Must be an array' })
ZodNever
// simple
z.never()
// advanced
z.union([z.string(), z.never()])
ZodRecord
// simple
z.record(z.string())
// advanced
z.record(z.string(), z.number())
ZodMap
// simple
z.map(z.string(), z.number())
// advanced
z.map(z.string(), z.object({ id: z.number() }))
ZodSet
// simple
z.set(z.number())
// advanced
z.set(z.string()).min(2)
ZodPromise
// simple
z.promise(z.string())
// advanced
z.promise(z.object({ id: z.number() }))
ZodTuple
// simple
z.tuple([z.string(), z.number()])
// advanced
z.tuple([z.string(), z.number(), z.boolean()])
ZodUnion
// simple
z.union([z.string(), z.number()])
// advanced
z.union([
  z.object({ type: z.literal('a'), value: z.string() }),
  z.object({ type: z.literal('b'), value: z.number() })
])
Nullable
// simple
z.string().nullable()
// advanced
z.number().min(0).nullable().default(null)
Optional
// simple
z.string().optional()
// advanced
z.object({ name: z.string().optional(), age: z.number() })
Regex
// simple
z.string().regex(/^abc/)
// advanced
z.string().regex(/^[A-Z]{3}-\d{4}$/)
Predefined functions (e.g. email, uuid)
// email
z.string().email()
// uuid
z.string().uuid()

Render

Render is a custom render function for the field. It will be used to render the field if you use a show mode. You can use it to create a custom view of the field, like a custom component or a custom HTML element.

{
  type: 'text',
  name: 'text',
  render: (value: string) => (
    <span className="size-32 bg-red-200">{value}</span>
  ),
}

Private

Private is a boolean that indicates if the field is private or not. If true, the field will not be visible in the form but can be used to store data. This is useful for fields that you want to keep hidden from the user but still need to store data.

{
  type: 'text',
  name: 'text',
  private: async () => {
    const res = await fetch(`/api/string`)
    const data = await res.json()
    return data.data
  },
}

Field interface

export type Size =
  | '1/2'
  | '1/3'
  | '1/4'
  | '1/6'
  | '2/3'
  | '3/4'
  | '5/6'
  | 'full'
 
interface CommonField {
  type: string
  name: string
  className?: string
  size?: Size
  label?: string | JSX.Element
  caption?: string | JSX.Element
  disabled?: boolean
  visible?: boolean
  validation?: z.ZodTypeAny
}

Field examples

{
  type: 'select',
  name: 'select',
  size: '1/4',
  caption: 'Select your favorite language',
  disabled: false,
  visible: true,
  options: {
    placeholder: 'Select a language',
    items: [
      { value: 'javascript', label: 'JavaScript' },
      { value: 'typescript', label: 'TypeScript' },
      { value: 'python', label: 'Python' }
    ]
  },
  validation: z.union([
    z.literal('javascript'),
    z.literal('typescript'),
    z.literal('python')
  ])
}