npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ttoss/forms

v0.41.1

Published

**@ttoss/forms** provides React form components built on [React Hook Form](https://react-hook-form.com/), with schema validation using [Zod](https://zod.dev/), integrated i18n support, and theme styling.

Readme

@ttoss/forms

@ttoss/forms provides React form components built on React Hook Form, with schema validation using Zod, integrated i18n support, and theme styling.

Note: Yup support is deprecated and will be removed in a future version. Please migrate to Zod for new projects.

Installation

pnpm i @ttoss/forms @ttoss/react-i18n @ttoss/ui @emotion/react
pnpm i -D @ttoss/i18n-cli

Note: This package is ESM only. I18n configuration is required—see @ttoss/react-i18n for setup details.

Quick Start

import { Button } from '@ttoss/ui';
import {
  Form,
  FormFieldCheckbox,
  FormFieldInput,
  useForm,
  z,
  zodResolver,
} from '@ttoss/forms';
import { I18nProvider } from '@ttoss/react-i18n';

const schema = z.object({
  firstName: z.string().min(1, 'First name is required'),
  age: z.number(),
  receiveEmails: z.boolean(),
});

export const FormComponent = () => {
  const formMethods = useForm({
    mode: 'all',
    resolver: zodResolver(schema),
  });

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <I18nProvider>
      <Form {...formMethods} onSubmit={onSubmit}>
        <FormFieldInput name="firstName" label="First Name" />
        <FormFieldInput name="age" label="Age" type="number" />
        <FormFieldCheckbox name="receiveEmails" label="Receive Emails" />
        <Button type="submit">Submit</Button>
      </Form>
    </I18nProvider>
  );
};

React Hook Form Integration

All React Hook Form APIs are re-exported from @ttoss/forms, including hooks like useForm, useController, useFieldArray, and useFormContext. See the React Hook Form documentation for complete API details.

Zod Validation (Recommended)

Import z and zodResolver directly from @ttoss/forms for schema validation using Zod:

import { Form, FormFieldInput, useForm, z, zodResolver } from '@ttoss/forms';

const schema = z.object({
  firstName: z.string().min(1, 'First name is required'),
});

const MyForm = () => {
  const formMethods = useForm({
    resolver: zodResolver(schema),
  });

  return (
    <Form {...formMethods} onSubmit={(data) => console.log(data)}>
      <FormFieldInput name="firstName" label="First Name" defaultValue="" />
      <Button type="submit">Submit</Button>
    </Form>
  );
};

Validation Messages

Invalid fields display default error messages like "Field is required". These messages are defined using i18n and can be customized for each locale.

Default Zod Messages

The package provides internationalized default messages for common Zod validation errors. These are automatically extracted when you run pnpm run i18n:

  • Required field: "Field is required"
  • Type mismatch: "Invalid Value for Field of type {expected}"
  • Minimum length: "Field must be at least {min} characters"

To customize these messages for your locale, extract the i18n messages and translate them in your application's i18n files (e.g., i18n/compiled/pt-BR.json). See the i18n-CLI documentation for more details.

Custom Validations

The package extends Zod with custom validation methods for Brazilian documents:

CPF Validation

import { z } from '@ttoss/forms';

const schema = z.object({
  cpf: z.string().cpf(), // Uses default message: "Invalid CPF"
  // Or with custom message:
  cpfCustom: z.string().cpf('CPF inválido'),
});

CNPJ Validation


```tsx
import { z } from '@ttoss/forms';

const schema = z.object({
  cnpj: z.string().cnpj(), // Uses default message: "Invalid CNPJ"
  // Or with custom message:
  cnpjCustom: z.string().cnpj('CNPJ inválido'),
});

Password Validation

import { passwordSchema } from '@ttoss/forms';
import { z } from '@ttoss/forms';

const schema = z.object({
  // Required password (minimum 8 characters)
  password: passwordSchema({ required: true }),

  // Optional password (accepts empty string or minimum 8 characters)
  optionalPassword: passwordSchema(),
});

Yup Validation (Deprecated)

DEPRECATION WARNING: Yup support is deprecated and will be removed in a future major version. Please migrate to Zod for new projects. Existing Yup schemas will continue to work, but we recommend planning your migration to Zod.

For legacy projects still using Yup, you can import yup and yupResolver from @ttoss/forms:

import { Form, FormFieldInput, useForm, yup, yupResolver } from '@ttoss/forms';

const schema = yup.object({
  firstName: yup.string().required(),
});

const MyForm = () => {
  const formMethods = useForm({
    resolver: yupResolver(schema),
  });

  return (
    <Form {...formMethods} onSubmit={(data) => console.log(data)}>
      <FormFieldInput name="firstName" label="First Name" />
      <Button type="submit">Submit</Button>
    </Form>
  );
};

Validation Approaches

There are two ways to validate form fields in @ttoss/forms: schema-based validation using Zod schemas with zodResolver, and field-level validation using the rules prop on individual form fields.

IMPORTANT: You cannot mix both validation methods for the same field—choose either schema-based or field-level validation per field.

When to use schema validation:

  • Cross-field validation
  • Complex business logic
  • Reusable validation patterns
  • Type-safe validation with TypeScript

When to use rules:

  • Simple, field-specific validations
  • Dynamic validation based on component state
  • Quick prototyping
  • Single-field conditional logic

1. Schema-based Validation (Recommended)

Use Zod schemas with zodResolver for complex validation logic:

import { z, zodResolver } from '@ttoss/forms';

const schema = z.object({
  email: z.string().email(),
  age: z.number().min(18).max(100),
});

const formMethods = useForm({
  resolver: zodResolver(schema),
});

Advantages:

  • Centralized validation logic
  • Type-safe with TypeScript
  • Reusable schemas
  • Complex validation patterns
  • Schema composition

2. Field-level Validation

Use the rules prop on individual form fields for simpler validations:

<FormFieldInput
  name="username"
  label="Username"
  rules={{
    required: 'Username is required',
    minLength: {
      value: 3,
      message: 'Username must be at least 3 characters',
    },
    pattern: {
      value: /^[a-zA-Z0-9_]+$/,
      message: 'Only letters, numbers, and underscores allowed',
    },
  }}
/>

<FormFieldInput
  name="email"
  label="Email"
  rules={{
    required: 'Email is required',
    validate: (value) => {
      return value.includes('@') || 'Invalid email format';
    },
  }}
/>

Available validation rules:

  • required: Field is required (string message or boolean)
  • min: Minimum value (for numbers)
  • max: Maximum value (for numbers)
  • minLength: Minimum string length
  • maxLength: Maximum string length
  • pattern: RegExp pattern
  • validate: Custom validation function or object of functions

Form Field Components

All form field components share common props:

  • name (required): Field name in the form
  • label: Field label text
  • disabled: Disables the field
  • defaultValue: Initial field value
  • tooltip: Label tooltip configuration
  • warning: Warning message displayed below the field
  • auxiliaryCheckbox: Optional auxiliary checkbox configuration
  • sx: Theme-UI styling object

Disabling Form Fields

You can disable form fields in two ways:

1. Disable the entire form:

Set disabled: true in useForm to disable all fields at once:

const formMethods = useForm({
  disabled: true, // Disables all fields
});

This is particularly useful for preventing user interaction during asynchronous operations:

const [isSubmitting, setIsSubmitting] = useState(false);

const formMethods = useForm({
  disabled: isSubmitting, // Disable form during submission
});

const onSubmit = async (data) => {
  setIsSubmitting(true);
  await saveData(data);
  setIsSubmitting(false);
};

2. Disable individual fields:

Use the disabled prop on specific form field components:

<FormFieldInput name="email" label="Email" disabled />

Field-level disabled props override the form-level setting:

const formMethods = useForm({
  disabled: false,
});

// This field will be disabled even though the form is enabled
<FormFieldInput name="id" label="ID" disabled />;

Auxiliary Checkbox

Form fields can include an optional auxiliary checkbox rendered between the field and error message. This is useful for input confirmation, terms acceptance, or conditional display of other fields.

<FormFieldInput
  name="email"
  label="Email"
  auxiliaryCheckbox={{
    name: 'confirmEmail',
    label: 'Send me promotional emails',
  }}
/>

The auxiliary checkbox can be disabled independently of the main field:

<FormFieldInput
  name="email"
  label="Email"
  auxiliaryCheckbox={{
    name: 'confirmEmail',
    label: 'Send me promotional emails',
    disabled: true, // Checkbox is disabled, but email field is enabled
  }}
/>

Props for auxiliaryCheckbox:

  • name (required): Field name for the checkbox
  • label (required): Checkbox label text
  • disabled: Disables the checkbox (independent of field disabled state)
  • defaultValue: Initial checkbox value

The auxiliary checkbox's disabled state is the logical OR of its own disabled prop and the field's disabled state.

FormFieldInput

Text input field supporting all HTML input types.

<FormFieldInput
  name="email"
  label="Email"
  type="email"
  placeholder="Enter your email"
/>

FormFieldPassword

Password input with show/hide toggle.

<FormFieldPassword name="password" label="Password" />

FormFieldTextarea

Multi-line text input.

<FormFieldTextarea name="description" label="Description" rows={4} />

FormFieldCheckbox

Single checkbox or checkbox group.

<FormFieldCheckbox name="terms" label="I accept the terms" />

FormFieldSwitch

Toggle switch component.

<FormFieldSwitch name="notifications" label="Enable notifications" />

FormFieldRadio

Radio button group.

const options = [
  { value: 'option1', label: 'Option 1' },
  { value: 'option2', label: 'Option 2' },
];

<FormFieldRadio name="choice" label="Choose one" options={options} />;

FormFieldRadioCard

Radio buttons styled as cards.

<FormFieldRadioCard name="plan" label="Select Plan" options={options} />

FormFieldRadioCardIcony

Radio cards with icon support.

const options = [
  { value: 'card', label: 'Credit Card', icon: 'credit-card' },
  { value: 'bank', label: 'Bank Transfer', icon: 'bank' },
];

<FormFieldRadioCardIcony
  name="payment"
  label="Payment Method"
  options={options}
/>;

FormFieldSelect

Dropdown select field.

const options = [
  { value: 'ferrari', label: 'Ferrari' },
  { value: 'mercedes', label: 'Mercedes' },
  { value: 'bmw', label: 'BMW' },
];

<FormFieldSelect name="car" label="Choose a car" options={options} />;

FormFieldSelect Default Values

FormFieldSelect defaults to the first option or a specified defaultValue:

// Defaults to "Ferrari"
<FormFieldSelect name="car" label="Cars" options={options} />

// Defaults to "Mercedes"
<FormFieldSelect
  name="car"
  label="Cars"
  options={options}
  defaultValue="Mercedes"
/>

FormFieldSegmentedControl

Segmented control field wrapping the SegmentedControl from @ttoss/ui. Use this field when you want a compact set of mutually-exclusive options presented as segmented buttons.

Props are the common form field props plus any SegmentedControl props (except value and className). Notably, the variant prop is supported and defaults to "secondary" if not provided.

<FormFieldSegmentedControl
  name="viewMode"
  label="View"
  options={[
    { value: 'list', label: 'List' },
    { value: 'grid', label: 'Grid' },
  ]}
  variant="primary" // optional, defaults to "secondary"
  defaultValue="list"
/>

When using a placeholder, an empty option is automatically added if not present:

<FormFieldSelect
  name="car"
  label="Cars"
  options={options}
  placeholder="Select a car"
/>

Note: placeholder and defaultValue cannot be used together.

For dynamic options from async data, reset the field after loading:

const { resetField } = useForm();
const [options, setOptions] = useState([]);

useEffect(() => {
  // Fetch options
  const fetchedOptions = await fetchData();
  setOptions(fetchedOptions);
  resetField('car', { defaultValue: 'Ferrari' });
}, []);

FormFieldNumericFormat

Numeric input with formatting support (decimals, thousands separators).

<FormFieldNumericFormat
  name="price"
  label="Price"
  thousandSeparator=","
  decimalScale={2}
/>

FormFieldCurrencyInput

Currency input with locale-based formatting. The decimal and thousand separators are automatically determined by the locale set in the I18nProvider.

<FormFieldCurrencyInput name="amount" label="Amount" prefix="$" />

Customizing Separators per Locale

The component uses i18n messages to determine the decimal and thousand separators based on the current locale. You can customize these for each locale in your application:

  1. First, extract the i18n messages by running pnpm run i18n in your package
  2. In your application's i18n files (e.g., i18n/compiled/pt-BR.json), add the custom separators:
{
  "JnCaDG": ",", // Decimal separator (default: ".")
  "0+4wTp": "." // Thousand separator (default: ",")
}

This approach allows each locale to define its own number formatting rules, which will be automatically applied to all currency inputs.

For more information about the i18n workflow, see the i18n-CLI documentation.

FormFieldPatternFormat

Input with custom format patterns.

<FormFieldPatternFormat
  name="phone"
  label="Phone"
  format="+1 (###) ###-####"
  mask="_"
/>

FormFieldCreditCardNumber

Credit card input with automatic formatting.

<FormFieldCreditCardNumber name="cardNumber" label="Card Number" />

Brazil-Specific Fields

Import from @ttoss/forms/brazil:

import {
  FormFieldCEP,
  FormFieldCNPJ,
  FormFieldPhone,
} from '@ttoss/forms/brazil';

FormFieldCEP

Brazilian postal code (CEP) input with automatic formatting.

<FormFieldCEP name="cep" label="CEP" />

FormFieldCNPJ

Brazilian tax ID (CNPJ) input with validation and formatting.

<FormFieldCNPJ name="cnpj" label="CNPJ" />

The package also exports isCnpjValid(cnpj: string) for standalone validation.

FormFieldPhone

Brazilian phone number input with formatting.

<FormFieldPhone name="phone" label="Phone" />

FormGroup

Groups related fields with optional label and layout direction.

<FormGroup label="Personal Information" direction="row">
  <FormFieldInput name="firstName" label="First Name" />
  <FormFieldInput name="lastName" label="Last Name" />
</FormGroup>

<FormGroup label="Address">
  <FormFieldInput name="street" label="Street" />
  <FormFieldInput name="city" label="City" />
</FormGroup>

Props:

  • label: Group label
  • direction: Layout direction ('row' | 'column')
  • name: Group name for error messages

Multistep Forms

Import from @ttoss/forms/multistep-form:

import { MultistepForm } from '@ttoss/forms/multistep-form';
import { FormFieldInput, z } from '@ttoss/forms';

const steps = [
  {
    label: 'Step 1',
    question: 'What is your name?',
    fields: <FormFieldInput name="name" label="Name" />,
    schema: z.object({
      name: z.string().min(1, 'Name is required'),
    }),
  },
  {
    label: 'Step 2',
    question: 'How old are you?',
    fields: <FormFieldInput type="number" name="age" label="Age" />,
    defaultValues: { age: 18 },
    schema: z.object({
      age: z.number().min(18, 'Must be at least 18'),
    }),
  },
];

const MyForm = () => {
  return (
    <MultistepForm
      steps={steps}
      onSubmit={(data) => console.log(data)}
      footer="© 2024 Company"
      header={{
        variant: 'logo',
        src: '/logo.png',
        onClose: () => console.log('closed'),
      }}
    />
  );
};

MultistepForm Props

  • steps: Array of step configurations
  • onSubmit: Handler called with complete form data
  • footer: Footer text
  • header: Header configuration (see below)

Step Configuration

Each step object contains:

  • label: Step label for navigation
  • question: Question or instruction text
  • fields: React element(s) containing form fields
  • schema: Zod validation schema
  • defaultValues: Optional default values for step fields

Header Types

Logo Header:

{
  variant: 'logo',
  src: '/path/to/logo.png',
  onClose: () => console.log('Close clicked')
}

Titled Header:

{
  variant: 'titled',
  title: 'Form Title',
  leftIcon: 'arrow-left',
  rightIcon: 'close',
  onLeftIconClick: () => console.log('Back'),
  onRightIconClick: () => console.log('Close')
}