Files
skills/docs/ui/forms/react-forms
2026-05-29 08:20:56 +01:00
..
2026-05-29 08:20:56 +01:00
2026-05-29 08:20:56 +01:00

@suaveplan/react-forms - React forms library — ApiClient/Auth contexts, ValidationEngine, useFormValidation/useDomainForm with field rendering, createDomainForm factory (with three escape hatches), four recipes (wizard, repeating-fieldset, conditional, async-search), MoneyInput + Field re-exports, and a TranslationKey-typed validator suite (sync + async + composers).

Summary

React forms library for the Suaveplan ecosystem. Wraps react-hook-form + Zod with a primitives + factory + recipes architecture: every user-facing string is typed TranslationKey, every error code is registered with @suaveplan/error, every lifecycle event emits OTel telemetry.

Wave 5b adds the field-rendering layer, the createDomainForm factory (with three escape hatches), four recipes (wizard, repeating-fieldset, conditional, async-search), and the MoneyInput primitive on top of Wave 5a's contexts + validation engine + validator suite.

Documentation tree

  • api-client-context — (no submodules) - use-api-client — React hook that reads the active `…
    • api-client-context — (no submodules) - use-api-client — React hook that reads the active `…
    • use-api-client — React hook that reads the active ApiClient from the surrounding <ApiClientProvider> and returns it typed as the con…
  • auth-context — (no submodules) - use-auth — React hook that reads the active `AuthCo…
    • auth-context — (no submodules) - use-auth — React hook that reads the active `AuthCo…
    • use-auth — React hook that reads the active AuthContextValue from the surrounding <AuthProvider> and returns it typed as `Auth…
  • use-domain-form — (no submodules)
  • use-form-validation — (no submodules)
  • validation-engine — (no submodules)
    • cross-field-rule — Convenience factory + helper for declaring CrossFieldRule values. Cross-field rules are the second pipeline phase ins…
    • server-error-mapper — Bridges server-side validation envelopes into the engine's per-field ValidatorFailure shape. When a form's `submit.on…
    • validation-engine — (no submodules)
  • error-codes — Registers the thirteen stable error codes used by @suaveplan/react-forms under the REACT_FORMS namespace via `defin…
  • react-forms-error — Defines the base ReactFormsError class shared by every error path in @suaveplan/react-forms. Extending the project…
  • create-domain-form — Declarative typed-form factory. Wraps useDomainForm + <RHF FormProvider> + the layout dispatcher into a single comp…
    • create-domain-form — Declarative typed-form factory. Wraps useDomainForm + <RHF FormProvider> + the layout dispatcher into a single comp…
  • field-config — FieldConfig discriminated union, per-kind Zod schemas, and the validateFieldConfig / validateFieldConfigMap helpe…
    • field-config — FieldConfig discriminated union, per-kind Zod schemas, and the validateFieldConfig / validateFieldConfigMap helpe…
  • render-field — Single-field renderer used by useDomainForm and createDomainForm to turn a typed FieldConfig into a <Field> cha…
    • render-field — Single-field renderer used by useDomainForm and createDomainForm to turn a typed FieldConfig into a <Field> cha…
  • locales — Bundled translation strings shipped with @suaveplan/react-forms.
    • augmentation — Side-effect-only TypeScript module that augments @suaveplan/react-i18n's ContributedTranslationKeys interface with…
    • locales — Bundled translation strings shipped with @suaveplan/react-forms.
    • translation-keys — translation-keys.ts is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/r…
  • observability — (no submodules)
  • field — (no submodules)
    • field — (no submodules)
  • money-input — Locale-aware currency input wrapping @suaveplan/react-ui's <Input>.
    • money-input — Locale-aware currency input wrapping @suaveplan/react-ui's <Input>.
  • async-search — Async-prefilled form recipe. Resolves an entity by id via the supplied load(id, signal) callback, then mounts the u…
    • async-search — Async-prefilled form recipe. Resolves an entity by id via the supplied load(id, signal) callback, then mounts the u…
  • conditional — Show / hide form fields based on a predicate over the parent form's values. When the gate flips closed the recipe unreg…
    • conditional — Show / hide form fields based on a predicate over the parent form's values. When the gate flips closed the recipe unreg…
  • repeating-fieldset — Dynamic-array fieldset recipe backed by react-hook-form's useFieldArray. Renders one row per array entry; add / remov…
    • repeating-fieldset — Dynamic-array fieldset recipe backed by react-hook-form's useFieldArray. Renders one row per array entry; add / remov…
  • wizard — Multi-step wizard recipe. Each step owns a Zod sub-schema and a typed field-config map; navigating forward validates th…
    • wizard — Multi-step wizard recipe. Each step owns a Zod sub-schema and a typed field-config map; navigating forward validates th…
  • async-email-unique — (no submodules)
  • async-username-available — (no submodules)
  • compose — (no submodules) - or — OR-semantics composer for the validator suite.…
    • compose — (no submodules) - or — OR-semantics composer for the validator suite.…
    • or — OR-semantics composer for the validator suite. Returns a validator that passes when any of the input validators passes.…
    • sequence — Short-circuit AND composer with explicit ordering semantics. Identical to compose in result — every validator must pa…
    • shared — Tiny internal helper module shared by every composer in validation/compose. Centralises the Promise discrimination…
  • bic — (no submodules)
    • bic — (no submodules)
  • credit-card — (no submodules)
  • custom — (no submodules)
  • email — (no submodules)
    • email — (no submodules)
  • iban — (no submodules)
    • iban — (no submodules)
  • max-length — (no submodules)
  • min-length — (no submodules)
  • password-strength — (no submodules)
  • pattern — (no submodules)
  • phone — (no submodules)
    • phone — (no submodules)
  • postcode — (no submodules)
  • required — (no submodules)
  • url — (no submodules)
    • url — (no submodules)
- use-api-client — React hook that reads the active `…
  • api-client-context — (no submodules)- use-api-client — React hook that reads the active `…

  • use-api-client — React hook that reads the active ApiClient from the surrounding <ApiClientProvider> and returns it typed as the con…

  • auth-context — (no submodules)- use-auth — React hook that reads the active `AuthCo…

    • auth-context — (no submodules)- use-auth — React hook that reads the active `AuthCo…
    • use-auth — React hook that reads the active AuthContextValue from the surrounding <AuthProvider> and returns it typed as `Auth…
  • use-domain-form — (no submodules)- use-domain-form — (no submodules)- use-form-validation — (no submodules)- use-form-validation — (no submodules)- validation-engine — (no submodules)- cross-field-rule — Convenience factory + helper for declaring CrossFieldRule values. Cross-field rules are the second pipeline phase ins…

    • server-error-mapper — Bridges server-side validation envelopes into the engine's per-field ValidatorFailure shape. When a form's `submit.on…
    • validation-engine — (no submodules)- error-codes — Registers the thirteen stable error codes used by @suaveplan/react-forms under the REACT_FORMS namespace via `defin…
  • react-forms-error — Defines the base ReactFormsError class shared by every error path in @suaveplan/react-forms. Extending the project…

  • create-domain-form — Declarative typed-form factory. Wraps useDomainForm + <RHF FormProvider> + the layout dispatcher into a single comp…

    • create-domain-form — Declarative typed-form factory. Wraps useDomainForm + <RHF FormProvider> + the layout dispatcher into a single comp…
  • field-config — FieldConfig discriminated union, per-kind Zod schemas, and the validateFieldConfig / validateFieldConfigMap helpe…

    • field-config — FieldConfig discriminated union, per-kind Zod schemas, and the validateFieldConfig / validateFieldConfigMap helpe…
  • render-field — Single-field renderer used by useDomainForm and createDomainForm to turn a typed FieldConfig into a <Field> cha…

    • render-field — Single-field renderer used by useDomainForm and createDomainForm to turn a typed FieldConfig into a <Field> cha…
  • locales — Bundled translation strings shipped with @suaveplan/react-forms.

    • augmentation — Side-effect-only TypeScript module that augments @suaveplan/react-i18n's ContributedTranslationKeys interface with…
    • locales — Bundled translation strings shipped with @suaveplan/react-forms.
    • translation-keys — translation-keys.ts is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/r…
  • observability — (no submodules)- react-forms-metrics — (no submodules)- field — (no submodules)- field — (no submodules)- money-input — Locale-aware currency input wrapping @suaveplan/react-ui's <Input>.

    • money-input — Locale-aware currency input wrapping @suaveplan/react-ui's <Input>.
  • async-search — Async-prefilled form recipe. Resolves an entity by id via the supplied load(id, signal) callback, then mounts the u…

    • async-search — Async-prefilled form recipe. Resolves an entity by id via the supplied load(id, signal) callback, then mounts the u…
  • conditional — Show / hide form fields based on a predicate over the parent form's values. When the gate flips closed the recipe unreg…

    • conditional — Show / hide form fields based on a predicate over the parent form's values. When the gate flips closed the recipe unreg…
  • repeating-fieldset — Dynamic-array fieldset recipe backed by react-hook-form's useFieldArray. Renders one row per array entry; add / remov…

    • repeating-fieldset — Dynamic-array fieldset recipe backed by react-hook-form's useFieldArray. Renders one row per array entry; add / remov…
  • wizard — Multi-step wizard recipe. Each step owns a Zod sub-schema and a typed field-config map; navigating forward validates th…

    • wizard — Multi-step wizard recipe. Each step owns a Zod sub-schema and a typed field-config map; navigating forward validates th…
  • async-email-unique — (no submodules)- async-email-unique — (no submodules)- async-username-available — (no submodules)- async-username-available — (no submodules)- compose — (no submodules)- or — OR-semantics composer for the validator suite.…

    • compose — (no submodules)- or — OR-semantics composer for the validator suite.…
    • or — OR-semantics composer for the validator suite. Returns a validator that passes when any of the input validators passes.…
    • sequence — Short-circuit AND composer with explicit ordering semantics. Identical to compose in result — every validator must pa…
    • shared — Tiny internal helper module shared by every composer in validation/compose. Centralises the Promise discrimination…
  • bic — (no submodules)- bic — (no submodules)- credit-card — (no submodules)- credit-card — (no submodules)- custom — (no submodules)- custom — (no submodules)- email — (no submodules)- email — (no submodules)- iban — (no submodules)- iban — (no submodules)- max-length — (no submodules)- max-length — (no submodules)- min-length — (no submodules)- min-length — (no submodules)- password-strength — (no submodules)- password-strength — (no submodules)- pattern — (no submodules)- pattern — (no submodules)- phone — (no submodules)- phone — (no submodules)- postcode — (no submodules)- postcode — (no submodules)- required — (no submodules)- required — (no submodules)- url — (no submodules)- url — (no submodules)- use-api-client — React hook that reads the active `…

  • api-client-context — (no submodules)- use-api-client — React hook that reads the active `…

  • use-api-client — React hook that reads the active ApiClient from the surrounding <ApiClientProvider> and returns it typed as the con…

  • auth-context — (no submodules)- use-auth — React hook that reads the active `AuthCo…

    • auth-context — (no submodules)- use-auth — React hook that reads the active `AuthCo…
    • use-auth — React hook that reads the active AuthContextValue from the surrounding <AuthProvider> and returns it typed as `Auth…
  • use-domain-form — (no submodules)- use-domain-form — (no submodules)- use-form-validation — (no submodules)- use-form-validation — (no submodules)- validation-engine — (no submodules)- cross-field-rule — Convenience factory + helper for declaring CrossFieldRule values. Cross-field rules are the second pipeline phase ins…

    • server-error-mapper — Bridges server-side validation envelopes into the engine's per-field ValidatorFailure shape. When a form's `submit.on…
    • validation-engine — (no submodules)- error-codes — Registers the thirteen stable error codes used by @suaveplan/react-forms under the REACT_FORMS namespace via `defin…
  • react-forms-error — Defines the base ReactFormsError class shared by every error path in @suaveplan/react-forms. Extending the project…

  • create-domain-form — Declarative typed-form factory. Wraps useDomainForm + <RHF FormProvider> + the layout dispatcher into a single comp…

    • create-domain-form — Declarative typed-form factory. Wraps useDomainForm + <RHF FormProvider> + the layout dispatcher into a single comp…
  • field-config — FieldConfig discriminated union, per-kind Zod schemas, and the validateFieldConfig / validateFieldConfigMap helpe…

    • field-config — FieldConfig discriminated union, per-kind Zod schemas, and the validateFieldConfig / validateFieldConfigMap helpe…
  • render-field — Single-field renderer used by useDomainForm and createDomainForm to turn a typed FieldConfig into a <Field> cha…

    • render-field — Single-field renderer used by useDomainForm and createDomainForm to turn a typed FieldConfig into a <Field> cha…
  • locales — Bundled translation strings shipped with @suaveplan/react-forms.

    • augmentation — Side-effect-only TypeScript module that augments @suaveplan/react-i18n's ContributedTranslationKeys interface with…
    • locales — Bundled translation strings shipped with @suaveplan/react-forms.
    • translation-keys — translation-keys.ts is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/r…
  • observability — (no submodules)- react-forms-metrics — (no submodules)- field — (no submodules)- field — (no submodules)- money-input — Locale-aware currency input wrapping @suaveplan/react-ui's <Input>.

    • money-input — Locale-aware currency input wrapping @suaveplan/react-ui's <Input>.
  • async-search — Async-prefilled form recipe. Resolves an entity by id via the supplied load(id, signal) callback, then mounts the u…

    • async-search — Async-prefilled form recipe. Resolves an entity by id via the supplied load(id, signal) callback, then mounts the u…
  • conditional — Show / hide form fields based on a predicate over the parent form's values. When the gate flips closed the recipe unreg…

    • conditional — Show / hide form fields based on a predicate over the parent form's values. When the gate flips closed the recipe unreg…
  • repeating-fieldset — Dynamic-array fieldset recipe backed by react-hook-form's useFieldArray. Renders one row per array entry; add / remov…

    • repeating-fieldset — Dynamic-array fieldset recipe backed by react-hook-form's useFieldArray. Renders one row per array entry; add / remov…
  • wizard — Multi-step wizard recipe. Each step owns a Zod sub-schema and a typed field-config map; navigating forward validates th…

    • wizard — Multi-step wizard recipe. Each step owns a Zod sub-schema and a typed field-config map; navigating forward validates th…
  • async-email-unique — (no submodules)- async-email-unique — (no submodules)- async-username-available — (no submodules)- async-username-available — (no submodules)- compose — (no submodules)- or — OR-semantics composer for the validator suite.…

    • compose — (no submodules)- or — OR-semantics composer for the validator suite.…
    • or — OR-semantics composer for the validator suite. Returns a validator that passes when any of the input validators passes.…
    • sequence — Short-circuit AND composer with explicit ordering semantics. Identical to compose in result — every validator must pa…
    • shared — Tiny internal helper module shared by every composer in validation/compose. Centralises the Promise discrimination…
  • bic — (no submodules)- bic — (no submodules)- credit-card — (no submodules)- credit-card — (no submodules)- custom — (no submodules)- custom — (no submodules)- email — (no submodules)- email — (no submodules)- iban — (no submodules)- iban — (no submodules)- max-length — (no submodules)- max-length — (no submodules)- min-length — (no submodules)- min-length — (no submodules)- password-strength — (no submodules)- password-strength — (no submodules)- pattern — (no submodules)- pattern — (no submodules)- phone — (no submodules)- phone — (no submodules)- postcode — (no submodules)- postcode — (no submodules)- required — (no submodules)- required — (no submodules)- url — (no submodules)- url — (no submodules)- use-api-client — React hook that reads the active `…

  • api-client-context — (no submodules)- use-api-client — React hook that reads the active `…

  • use-api-client — React hook that reads the active ApiClient from the surrounding <ApiClientProvider> and returns it typed as the con…

  • auth-context — (no submodules)- use-auth — React hook that reads the active `AuthCo…

    • auth-context — (no submodules)- use-auth — React hook that reads the active `AuthCo…
    • use-auth — React hook that reads the active AuthContextValue from the surrounding <AuthProvider> and returns it typed as `Auth…
  • use-domain-form — (no submodules)- use-domain-form — (no submodules)- use-form-validation — (no submodules)- use-form-validation — (no submodules)- validation-engine — (no submodules)- cross-field-rule — Convenience factory + helper for declaring CrossFieldRule values. Cross-field rules are the second pipeline phase ins…

    • server-error-mapper — Bridges server-side validation envelopes into the engine's per-field ValidatorFailure shape. When a form's `submit.on…
    • validation-engine — (no submodules)- error-codes — Registers the thirteen stable error codes used by @suaveplan/react-forms under the REACT_FORMS namespace via `defin…
  • react-forms-error — Defines the base ReactFormsError class shared by every error path in @suaveplan/react-forms. Extending the project…

  • create-domain-form — Declarative typed-form factory. Wraps useDomainForm + <RHF FormProvider> + the layout dispatcher into a single comp…

    • create-domain-form — Declarative typed-form factory. Wraps useDomainForm + <RHF FormProvider> + the layout dispatcher into a single comp…
  • field-config — FieldConfig discriminated union, per-kind Zod schemas, and the validateFieldConfig / validateFieldConfigMap helpe…

    • field-config — FieldConfig discriminated union, per-kind Zod schemas, and the validateFieldConfig / validateFieldConfigMap helpe…
  • render-field — Single-field renderer used by useDomainForm and createDomainForm to turn a typed FieldConfig into a <Field> cha…

    • render-field — Single-field renderer used by useDomainForm and createDomainForm to turn a typed FieldConfig into a <Field> cha…
  • locales — Bundled translation strings shipped with @suaveplan/react-forms.

    • augmentation — Side-effect-only TypeScript module that augments @suaveplan/react-i18n's ContributedTranslationKeys interface with…
    • locales — Bundled translation strings shipped with @suaveplan/react-forms.
    • translation-keys — translation-keys.ts is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/r…
  • observability — (no submodules)- react-forms-metrics — (no submodules)- field — (no submodules)- field — (no submodules)- money-input — Locale-aware currency input wrapping @suaveplan/react-ui's <Input>.

    • money-input — Locale-aware currency input wrapping @suaveplan/react-ui's <Input>.
  • async-search — Async-prefilled form recipe. Resolves an entity by id via the supplied load(id, signal) callback, then mounts the u…

    • async-search — Async-prefilled form recipe. Resolves an entity by id via the supplied load(id, signal) callback, then mounts the u…
  • conditional — Show / hide form fields based on a predicate over the parent form's values. When the gate flips closed the recipe unreg…

    • conditional — Show / hide form fields based on a predicate over the parent form's values. When the gate flips closed the recipe unreg…
  • repeating-fieldset — Dynamic-array fieldset recipe backed by react-hook-form's useFieldArray. Renders one row per array entry; add / remov…

    • repeating-fieldset — Dynamic-array fieldset recipe backed by react-hook-form's useFieldArray. Renders one row per array entry; add / remov…
  • wizard — Multi-step wizard recipe. Each step owns a Zod sub-schema and a typed field-config map; navigating forward validates th…

    • wizard — Multi-step wizard recipe. Each step owns a Zod sub-schema and a typed field-config map; navigating forward validates th…
  • async-email-unique — (no submodules)- async-email-unique — (no submodules)- async-username-available — (no submodules)- async-username-available — (no submodules)- compose — (no submodules)- or — OR-semantics composer for the validator suite.…

    • compose — (no submodules)- or — OR-semantics composer for the validator suite.…
    • or — OR-semantics composer for the validator suite. Returns a validator that passes when any of the input validators passes.…
    • sequence — Short-circuit AND composer with explicit ordering semantics. Identical to compose in result — every validator must pa…
    • shared — Tiny internal helper module shared by every composer in validation/compose. Centralises the Promise discrimination…
  • bic — (no submodules)- bic — (no submodules)- credit-card — (no submodules)- credit-card — (no submodules)- custom — (no submodules)- custom — (no submodules)- email — (no submodules)- email — (no submodules)- iban — (no submodules)- iban — (no submodules)- max-length — (no submodules)- max-length — (no submodules)- min-length — (no submodules)- min-length — (no submodules)- password-strength — (no submodules)- password-strength — (no submodules)- pattern — (no submodules)- pattern — (no submodules)- phone — (no submodules)- phone — (no submodules)- postcode — (no submodules)- postcode — (no submodules)- required — (no submodules)- required — (no submodules)- url — (no submodules)- url — (no submodules)- api-client-context — (no submodules)- use-api-client — React hook that reads the active ApiClient from the surrounding <ApiClientProvider> and returns it typed as the con…

  • auth-context — (no submodules) - auth-context — (no submodules) - use-auth — React hook that reads the active AuthContextValue from the surrounding <AuthProvider> and returns it typed as `Auth…

  • use-domain-form — (no submodules) - use-domain-form — (no submodules)- use-form-validation — (no submodules) - use-form-validation — (no submodules)- validation-engine — (no submodules) - cross-field-rule — Convenience factory + helper for declaring CrossFieldRule values. Cross-field rules are the second pipeline phase ins…

    • server-error-mapper — Bridges server-side validation envelopes into the engine's per-field ValidatorFailure shape. When a form's `submit.on…
    • validation-engine — (no submodules)- error-codes — Registers the thirteen stable error codes used by @suaveplan/react-forms under the REACT_FORMS namespace via `defin…
  • react-forms-error — Defines the base ReactFormsError class shared by every error path in @suaveplan/react-forms. Extending the project…

  • create-domain-form — Declarative typed-form factory. Wraps useDomainForm + <RHF FormProvider> + the layout dispatcher into a single comp…

    • create-domain-form — Declarative typed-form factory. Wraps useDomainForm + <RHF FormProvider> + the layout dispatcher into a single comp…
  • field-config — FieldConfig discriminated union, per-kind Zod schemas, and the validateFieldConfig / validateFieldConfigMap helpe…

    • field-config — FieldConfig discriminated union, per-kind Zod schemas, and the validateFieldConfig / validateFieldConfigMap helpe…
  • render-field — Single-field renderer used by useDomainForm and createDomainForm to turn a typed FieldConfig into a <Field> cha…

    • render-field — Single-field renderer used by useDomainForm and createDomainForm to turn a typed FieldConfig into a <Field> cha…
  • locales — Bundled translation strings shipped with @suaveplan/react-forms.

    • augmentation — Side-effect-only TypeScript module that augments @suaveplan/react-i18n's ContributedTranslationKeys interface with…
    • locales — Bundled translation strings shipped with @suaveplan/react-forms.
    • translation-keys — translation-keys.ts is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/r…
  • observability — (no submodules) - react-forms-metrics — (no submodules)- field — (no submodules) - field — (no submodules)- money-input — Locale-aware currency input wrapping @suaveplan/react-ui's <Input>.

    • money-input — Locale-aware currency input wrapping @suaveplan/react-ui's <Input>.
  • async-search — Async-prefilled form recipe. Resolves an entity by id via the supplied load(id, signal) callback, then mounts the u…

    • async-search — Async-prefilled form recipe. Resolves an entity by id via the supplied load(id, signal) callback, then mounts the u…
  • conditional — Show / hide form fields based on a predicate over the parent form's values. When the gate flips closed the recipe unreg…

    • conditional — Show / hide form fields based on a predicate over the parent form's values. When the gate flips closed the recipe unreg…
  • repeating-fieldset — Dynamic-array fieldset recipe backed by react-hook-form's useFieldArray. Renders one row per array entry; add / remov…

    • repeating-fieldset — Dynamic-array fieldset recipe backed by react-hook-form's useFieldArray. Renders one row per array entry; add / remov…
  • wizard — Multi-step wizard recipe. Each step owns a Zod sub-schema and a typed field-config map; navigating forward validates th…

    • wizard — Multi-step wizard recipe. Each step owns a Zod sub-schema and a typed field-config map; navigating forward validates th…
  • async-email-unique — (no submodules) - async-email-unique — (no submodules)- async-username-available — (no submodules) - async-username-available — (no submodules)- compose — (no submodules) - compose — (no submodules) - or — OR-semantics composer for the validator suite. Returns a validator that passes when any of the input validators passes.…

    • sequence — Short-circuit AND composer with explicit ordering semantics. Identical to compose in result — every validator must pa…
    • shared — Tiny internal helper module shared by every composer in validation/compose. Centralises the Promise discrimination…
  • bic — (no submodules) - bic — (no submodules)- credit-card — (no submodules) - credit-card — (no submodules)- custom — (no submodules) - custom — (no submodules)- email — (no submodules) - email — (no submodules)- iban — (no submodules) - iban — (no submodules)- max-length — (no submodules) - max-length — (no submodules)- min-length — (no submodules) - min-length — (no submodules)- password-strength — (no submodules) - password-strength — (no submodules)- pattern — (no submodules) - pattern — (no submodules)- phone — (no submodules) - phone — (no submodules)- postcode — (no submodules) - postcode — (no submodules)- required — (no submodules) - required — (no submodules)- url — (no submodules) - url — (no submodules)

Install

bun add @suaveplan/react-forms

Peer deps: react ^18.0.0 || ^19.0.0, @suaveplan/react-ui (must be provided by the consumer; this package re-exports <Field> and imports <Input> / <Button> / <Skeleton> / etc. from it).

Workspace deps: @suaveplan/react-i18n (TranslationKey), @suaveplan/react-ui (component primitives), @suaveplan/error (ReactFormsError), @suaveplan/timing (async-validator debounce), @suaveplan/api-schemas, @suaveplan/i18n (currency / date formatters).

External catalog deps: react-hook-form, @hookform/resolvers, zod, libphonenumber-js, @zxcvbn-ts/core, @opentelemetry/api.

Quick start — createDomainForm

The factory is the 80%-case ergonomics layer. Declare a Zod schema + field config + submit handler:

import { createDomainForm } from "@suaveplan/react-forms";
import { RegisterInput } from "@suaveplan/api-schemas";

const RegisterForm = createDomainForm({
    formId: "register",
    schema: RegisterInput,
    fields: {
        email: { kind: "email", label: "auth.email.label" as never },
        password: {
            kind: "password",
            label: "auth.password.label" as never,
            strengthMeter: true,
        },
        name: { kind: "text", label: "auth.name.label" as never },
    },
    defaultValues: { email: "", password: "", name: "" },
    submit: {
        label: "auth.register.submit" as never,
        onSubmit: async (values) => {
            await fetch("/api/auth/register", {
                method: "POST",
                body: JSON.stringify(values),
            });
        },
    },
});

export function RegisterPage(): JSX.Element {
    return <RegisterForm />;
}

Three escape hatches keep RHF visible:

// 1. Render-prop children for full custom shells:
<RegisterForm>
    {({ form, fields, submit, isSubmitting }) => (
        <CustomLayout>{Object.values(fields)}</CustomLayout>
    )}
</RegisterForm>

// 2. Static <Form.Field name=...> for partial layouts:
<div>
    <RegisterForm.Field name="email" />
    <p>Then choose a password.</p>
    <RegisterForm.Field name="password" />
</div>

// 3. The standalone useDomainForm hook, exported separately:
const { form, fields, submit } = useDomainForm({ ...config });

Recipes

Wizard — multi-step factory

import { createWizardForm } from "@suaveplan/react-forms";

const SignUpWizard = createWizardForm({
    formId: "signup",
    steps: [
        {
            id: "credentials",
            title: "auth.steps.credentials" as never,
            schema: z.object({ email: z.string().email(), password: z.string().min(8) }),
            fields: {
                email: { kind: "email", label: "auth.email.label" as never },
                password: { kind: "password", label: "auth.password.label" as never },
            },
        },
        {
            id: "profile",
            title: "auth.steps.profile" as never,
            schema: z.object({ name: z.string().min(1) }),
            fields: {
                name: { kind: "text", label: "auth.name.label" as never },
            },
        },
    ],
    onComplete: async (values) => api.auth.register(values),
});

Repeating fieldset — dynamic-array rows

import { createRepeatingFieldset } from "@suaveplan/react-forms";

const Contacts = createRepeatingFieldset<{ name: string; email: string }>({
    formId: "checkout",
    name: "contacts",
    minRows: 1,
    maxRows: 5,
    defaultRow: { name: "", email: "" },
    fieldset: {
        name: { kind: "text", label: "checkout.contact.name" as never },
        email: { kind: "email", label: "checkout.contact.email" as never },
    },
});

Conditional — show/hide fields

import { ConditionalField } from "@suaveplan/react-forms";

<ConditionalField<CheckoutValues>
    when={(v) => v.paymentMethod === "transfer"}
    names={["bankAccount"]}
    watch={["paymentMethod"]}
>
    <RegisterForm.Field name="bankAccount" />
</ConditionalField>

When the gate flips closed the recipe unregisters bankAccount from the form so it disappears from the submit payload.

Async search — load-then-edit

import { createAsyncSearchForm } from "@suaveplan/react-forms";

const EditUserForm = createAsyncSearchForm({
    formId: "edit-user",
    schema,
    fields: {
        email: { kind: "email", label: "user.email.label" as never },
        name: { kind: "text", label: "user.name.label" as never },
    },
    load: async (id, signal) => {
        const res = await fetch(`/api/users/${id}`, { signal });
        return (await res.json()) as { email: string; name: string };
    },
    submit: {
        label: "user.save" as never,
        onSubmit: async (values) => {
            await fetch(`/api/users/${id}`, {
                method: "PUT",
                body: JSON.stringify(values),
            });
        },
    },
});

<EditUserForm id="alice" />;

Validation library — sync + async + composers

import {
    compose,
    email,
    minLength,
    passwordStrength,
    required,
    sequence,
} from "@suaveplan/react-forms";

const validateEmail = sequence(required(), email());
const validatePassword = compose(
    required(),
    minLength(8),
    passwordStrength({ minScore: 3 })
);

validateEmail("alice@example.com"); // → { valid: true }
validatePassword("hunter2"); // → { valid: false, messageKey, messageArgs }

Async validators receive an AbortSignal:

import { asyncEmailUnique } from "@suaveplan/react-forms";

const checkEmailUnique = asyncEmailUnique({
    check: async (email, signal) => {
        const res = await fetch(
            `/api/users/email-available?email=${encodeURIComponent(email)}`,
            { signal }
        );
        return res.ok;
    },
    debounceMs: 300,
});

Standalone hooks

useDomainForm is exported separately for consumers building bespoke shells:

import { useDomainForm } from "@suaveplan/react-forms";

const { form, fields, submit, isSubmitting, serverError } = useDomainForm({
    formId: "login",
    schema,
    fields, // optional — when omitted, `fields` is null
    onSubmit: async (values) => api.auth.login(values),
});

Field kinds shipped in Wave 5b

text, email, password (with optional strengthMeter), number, currency (uses MoneyInput), checkbox, toggle, radio, textarea, custom (consumer render-prop).

Deferred primitives — Wave 5c roadmap

The following primitives and field kinds are NOT in Wave 5b. They ship in Wave 5c once the underlying react-ui components land:

Primitive Underlying component Wave
accessible-date-picker Calendar (react-ui) Wave 5c after react-ui Wave 3h
date-time-picker Calendar + TimeInput Wave 5c after react-ui Wave 3h
searchable-select Combobox (react-ui) Wave 5c after react-ui Wave 3h
tag-input Badge (react-ui) Wave 5c after react-ui Wave 3d
code-input InputOTP (react-ui) Wave 5c after react-ui Wave 3h
file-upload-field @suaveplan/react-file-upload Wave 5c after react-file-upload ships
rich-text-field @suaveplan/react-rich-text Wave 5c after react-rich-text ships

Field kinds excluded from the Wave 5b enum: date, dateTime, select, multi-select, combobox, file, rich-text, otp. Passing one of these to createDomainForm today fails the discriminator and throws REACT_FORMS.FIELD_KIND_INVALID at module-eval time.

Modules