antd-typed

Factory Hooks

Create reusable typed hooks with createFormHook.

When multiple components need to work with the same form schema, createFormHook generates a set of hooks that share type information. This is useful for large forms split across files or when building a library of form components.

Why Factory Hooks?

With useForm, each component that calls the hook gets its own form instance. This works for single-file forms but breaks down when:

  • Multiple components need to read/write the same form
  • You want to enforce a specific schema across related components
  • Sub-components need type-safe access to nested fields

createFormHook solves this by creating paired hooks: one to create the form, one to access it from anywhere in the tree.

Basic Usage

import React from "react";
import {  } from "zod";
import {  } from "antd-typed";
import { , ,  } from "antd";

const  = .({
  : .().(1),
  : .().(),
  : .({
    : .(),
    : .(),
  }),
});

// Create typed hooks for this schema
export const {
  : ,
  : ,
} = ({
  : ,
});

This creates two hooks:

HookPurpose
useUserFormCreates the form, returns Form, FormItem, etc.
useUserFormInstanceAccesses the form from child components

Using the Hooks

The parent component creates the form:

function () {
  const { ,  } = ({
    : () => {
      // values is fully typed from userSchema
      .();
    },
  });

  return (
    < ="vertical">
      < ="name" ="Name">
        < />
      </>
      < ="email" ="Email">
        < />
      </>
      < />
      < ="primary" ="submit">
        Submit
      </>
    </>
  );
}

Child components access the form with scoped types:

function () {
  // inheritAt scopes types to the address object
  const {  } = ({ : ["address"] });

  return (
    <>
      {/* Only address fields are valid here */}
      < ="street" ="Street">
        < />
      </>
      < ="city" ="City">
        < />
      </>
    </>
  );
}

The inheritAt option narrows the available field names to just those under address. Using an invalid name is a type error:

function () {
  const {  } = ({ : ["address"] });
  // Only "street" | "city" are valid here
  return (
    <>
      < ="street">
        < />
      </>
      < name="name">
Type '"name"' is not assignable to type '"street" | "city" | ["street"] | ["city"] | undefined'.
< /> </> </> ); }

inheritAt vs inherit

There are two ways to scope child components:

inheritAt (Explicit)

Specify the exact path. Types are checked at compile time.

function () {
  const {  } = ({ : ["address"] });
  // FormItem only accepts "street" | "city"
  return (
    < ="street">
      < />
    </>
  );
}

inherit (Dynamic)

Read the path from context. More flexible but less type-safe.

import React from "react";
import { useFormInstance } from "antd-typed";
import { Input } from "antd";

type Address = { street: string; city: string };

function AddressFields() {
  // Type is provided manually
  const { FormItem } = useFormInstance<Address>({ inherit: true });
  return (
    <FormItem name="street">
      <Input />
    </FormItem>
  );
}

Use inheritAt when the component is tied to a specific form. Use inherit when the component is generic and reused across forms.

Watching Values

Both hooks provide a typed useWatch for reactive field access:

function () {
  const {  } = ();
  const name = ("name");
const name: string | undefined
return <>Hello, { ?? "stranger"}</>; }

Project Organization

A common pattern is to define form hooks in a shared file:

src/
  forms/
    user-form.ts      # createFormHook for user schema
    company-form.ts   # createFormHook for company schema
  components/
    UserForm.tsx      # Uses useUserForm
    AddressSection.tsx # Uses useUserFormInstance
// forms/user-form.ts
import { createFormHook } from "antd-typed";
import { userSchema } from "../schemas";

export const {
  useCustomForm: useUserForm,
  useCustomFormInstance: useUserFormInstance,
} = createFormHook({ validator: userSchema });

Components import the specific hooks they need, getting full type safety tied to that schema.

useForm vs createFormHook

FeatureuseFormcreateFormHook
Single component formsYesOverkill
Multi-component formsWorks with useFormInstancePreferred
Type-safe inheritAtNoYes
Reusable across filesLimitedDesigned for it

For simple forms contained in one component, useForm is sufficient. For forms split across multiple components or files, createFormHook provides better type safety and organization.

On this page