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

@veams/form

v0.5.0

Published

Form state handlers and React bindings for the VEAMS StatusQuo ecosystem.

Readme

@veams/form

Form state handlers plus optional React bindings for the VEAMS StatusQuo ecosystem.

This package keeps form state generic at the root entrypoint and ships React-only helpers under @veams/form/react.

Docs

Live docs:

https://veams.github.io/status-quo/packages/form/overview

Install

npm install @veams/form @veams/status-quo react

Package Exports

Root exports:

  • FormStateHandler
  • FormActions
  • FormErrors
  • FormFieldName
  • FormState
  • FormStateHandlerConfig
  • FormStateHandlerOptions
  • FormTouched
  • FormValues
  • ValidatorFn

React entrypoint:

  • @veams/form/react
  • FormProvider
  • useFormController
  • useFieldMeta
  • useUncontrolledField
  • Controller

Validator adapters:

  • @veams/form/validators
  • @veams/form/validators/zod
  • toZodValidator(schema)

Quickstart

Create a generic form handler:

import { FormStateHandler } from '@veams/form';

type LoginValues = {
  email: string;
  password: string;
};

const loginForm = new FormStateHandler<LoginValues>({
  initialValues: {
    email: '',
    password: '',
  },
  validator: (values) => {
    const errors: Partial<Record<keyof LoginValues, string>> = {};

    if (!values.email) {
      errors.email = 'Email is required';
    }

    if (!values.password) {
      errors.password = 'Password is required';
    }

    return errors;
  },
});

loginForm.setFieldValue('email', '[email protected]');
loginForm.validateForm();

Nested values are supported through dot-path field names:

type ProfileForm = {
  profile: {
    email: string;
  };
};

const profileForm = new FormStateHandler<ProfileForm>({
  initialValues: {
    profile: {
      email: '',
    },
  },
});

profileForm.setFieldValue('profile.email', '[email protected]');
profileForm.setFieldTouched('profile.email', true);

React Quickstart

Use FormProvider to own one handler instance locally and useUncontrolledField() to bind native elements:

import { FormProvider, useUncontrolledField } from '@veams/form/react';

function EmailField() {
  const { meta, registerProps } = useUncontrolledField('email');

  return (
    <label>
      Email
      <input {...registerProps} type="email" />
      {meta.showError ? <span>{meta.error}</span> : null}
    </label>
  );
}

function LoginForm() {
  return (
    <FormProvider
      initialValues={{ email: '', password: '' }}
      onSubmit={async (values) => {
        await submitLogin(values);
      }}
      validator={(values) => ({
        ...(values.email ? {} : { email: 'Email is required' }),
        ...(values.password ? {} : { password: 'Password is required' }),
      })}
    >
      <EmailField />
      <button type="submit">Sign in</button>
    </FormProvider>
  );
}

Uncontrolled Field Principle

Native fields should stay uncontrolled by default in VEAMS Form, while FormStateHandler remains the source of truth for values, errors, touched state, and submit state.

Why this default is useful:

  • Lower render churn: typing updates the DOM directly without forcing controlled React value props on every keystroke.
  • Native behavior stays intact: browser input semantics, selection handling, and autofill work naturally.
  • Cleaner component code: field components mostly spread registerProps and render meta.
  • Clear ownership boundaries: feature/form behavior stays in the handler, React stays a binding layer.

When a component requires controlled props (value + onChange), use Controller intentionally for that field only.

Feature-Owned Form State

A feature handler can own the form handler and pass it into the React provider. This keeps cross-field validation and non-form UI state in the same feature boundary. When formHandlerInstance is provided, initialValues and validator stay on the handler and are not passed to FormProvider.

import { SignalStateHandler } from '@veams/status-quo';
import { FormStateHandler } from '@veams/form';

type LoginValues = {
  email: string;
  password: string;
};

type LoginState = {
  isPasswordVisible: boolean;
};

type LoginActions = {
  getFormHandler: () => FormStateHandler<LoginValues>;
  submitLogin: (values: LoginValues) => Promise<void>;
  togglePasswordVisibility: () => void;
};

class LoginStateHandler extends SignalStateHandler<LoginState, LoginActions> {
  private readonly formHandler = new FormStateHandler<LoginValues>({
    initialValues: {
      email: '',
      password: '',
    },
    validator: (values) => ({
      ...(values.email ? {} : { email: 'Email is required' }),
      ...(values.password ? {} : { password: 'Password is required' }),
    }),
  });

  constructor() {
    super({
      initialState: {
        isPasswordVisible: false,
      },
    });
  }

  getActions(): LoginActions {
    return {
      getFormHandler: () => this.formHandler,
      submitLogin: async (_values) => undefined,
      togglePasswordVisibility: () => {
        this.setState({
          isPasswordVisible: !this.getState().isPasswordVisible,
        });
      },
    };
  }
}
import { useStateFactory } from '@veams/status-quo/react';
import { FormProvider, useUncontrolledField } from '@veams/form/react';

function PasswordField({ isVisible }: { isVisible: boolean }) {
  const { meta, registerProps } = useUncontrolledField('password', {
    type: isVisible ? 'text' : 'password',
  });

  return (
    <label>
      Password
      <input {...registerProps} />
      {meta.showError ? <span>{meta.error}</span> : null}
    </label>
  );
}

function LoginFeature() {
  const [state, actions] = useStateFactory(() => new LoginStateHandler(), []);

  return (
    <FormProvider
      formHandlerInstance={actions.getFormHandler()}
      onSubmit={actions.submitLogin}
    >
      <PasswordField isVisible={state.isPasswordVisible} />
      <button onClick={actions.togglePasswordVisibility} type="button">
        Toggle password visibility
      </button>
      <button type="submit">Sign in</button>
    </FormProvider>
  );
}

Controlled Components

Use Controller when a third-party field expects value and onChange instead of native uncontrolled props.

import { Controller, FormProvider } from '@veams/form/react';

function ControlledRoleSelect() {
  return (
    <Controller
      name="role"
      render={({ field, fieldState }) => (
        <>
          <RoleSelect onBlur={field.onBlur} onChange={field.onChange} value={field.value as string} />
          {fieldState.touched && fieldState.error ? <span>{fieldState.error}</span> : null}
        </>
      )}
    />
  );
}

function RoleForm() {
  return (
    <FormProvider
      initialValues={{ role: 'user' }}
      onSubmit={(values) => saveRole(values.role)}
    >
      <ControlledRoleSelect />
      <button type="submit">Save</button>
    </FormProvider>
  );
}

Form-Level Submit Errors

Keep backend errors that are not tied to one field out of the field error map. Use setSubmitError() for those cases and read aggregate state through useFormMeta().

import { FormProvider, useFormMeta } from '@veams/form/react';

function SubmitErrorBanner() {
  const { submitError } = useFormMeta<{ email: string; password: string }>();

  return submitError ? <p role="alert">{submitError}</p> : null;
}

Schema Validators (Zod)

@veams/form does not depend on Zod, but it exposes a lightweight adapter for Zod-style safeParse schemas. The package currently includes only the Zod adapter because that is the most common schema setup in current usage. PRs for additional adapters are welcome as long as the package remains dependency-free.

import { z } from 'zod';
import { FormStateHandler } from '@veams/form';
import { toZodValidator } from '@veams/form/validators/zod';

const loginSchema = z.object({
  email: z.string().min(1, 'Email is required').email('Enter a valid email address'),
  password: z.string().min(12, 'Use at least 12 characters'),
});

type LoginValues = z.infer<typeof loginSchema>;

const form = new FormStateHandler<LoginValues>({
  initialValues: {
    email: '',
    password: '',
  },
  validator: toZodValidator(loginSchema),
});