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

@mapples/form

v1.0.4

Published

A comprehensive form management library for React and React Native with TypeScript support, validation, and advanced features like form history management.

Readme

@mapples/form

A comprehensive form management library for React and React Native with TypeScript support, validation, and advanced features like form history management.

Features

  • 🎯 Type-safe - Full TypeScript support with generic types
  • Validation - Yup schema validation integration
  • 📝 Form History - Built-in undo/redo functionality
  • 🔄 Reactive State - Subscription-based reactive updates
  • 🎣 Hook-based API - Comprehensive hooks for form management
  • 🏗️ Nested Objects - Support for complex nested data structures
  • 📱 Cross-platform - Works with React and React Native
  • 👆 Touch Tracking - Built-in field touch state management

Installation

npm install @mapples/form yup
# or
yarn add @mapples/form yup

Quick Start

import { Form, useFormField, useFormSubmit } from '@mapples/form';
import * as yup from 'yup';

// Define your form schema
const userSchema = yup.object({
  name: yup.string().required('Name is required'),
  email: yup.string().email('Invalid email').required('Email is required'),
  age: yup.number().min(18, 'Must be at least 18').required('Age is required'),
});

interface UserForm {
  name: string;
  email: string;
  age: number;
}

// Form component
const UserForm = () => (
  <Form<UserForm>
    initialValues={{ name: '', email: '', age: 0 }}
    validationSchema={userSchema}
    onSubmit={async (values) => {
      console.log('Submitting:', values);
      // Handle form submission
    }}
  >
    <NameField />
    <EmailField />
    <AgeField />
    <SubmitButton />
  </Form>
);

// Individual field components
const NameField = () => {
  const { value, error, isTouched, setValue, touch } = useFormField<string>(
    'name',
    '',
  );

  return (
    <div>
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onBlur={() => touch('name')}
        placeholder="Enter your name"
      />
      {isTouched && error && <span className="error">{error}</span>}
    </div>
  );
};

const SubmitButton = () => {
  const { submitting, submitForm } = useFormSubmit();

  return (
    <button onClick={submitForm} disabled={submitting}>
      {submitting ? 'Submitting...' : 'Submit'}
    </button>
  );
};

API Reference

Components

<Form<T>>

The main form component that provides form context to its children.

Props:

  • initialValues: T - Initial values for the form
  • validationSchema?: ObjectSchema<T> - Optional Yup validation schema
  • onSubmit?: (values: T) => void | Promise<void> - Form submission handler
  • ref?: RefObject<FormState<T> | null> - Optional ref to access FormState directly
  • historyItems?: number - Number of history items for undo/redo (0 to disable)
  • historyItemThrottle?: number - Throttle for saving history items (default: 5)
  • filterHistoryKeys?: string[] - Field paths to exclude from history tracking
  • onChange?: (values: T) => void - Callback for form value changes

Hooks

useFormContext()

Access the form context containing form state and methods.

const {
  form,
  submitting,
  submitForm,
  validationErrors,
  validate,
  touch,
  touched,
} = useFormContext();

useFormField<T>(fieldPath, fallbackValue?, subscribe?)

Manage a single form field with value, validation, and touch state.

const { value, error, errors, isTouched, setValue, validate, touch } =
  useFormField<string>('name', '');

Parameters:

  • fieldPath: string - Dot notation path to the field (e.g., 'user.name')
  • fallbackValue?: T - Default value if field is undefined
  • subscribe?: boolean - Whether to subscribe to real-time changes (default: true)

useFormValue<T>(fieldPath?)

Get a reactive value from the form state.

const userName = useFormValue<string>('user.name');
const allValues = useFormValue<UserForm>(); // Gets all form values

useFormState<T>()

Manage the entire form state with reactive values and control methods.

const { values, setValues, getValues } = useFormState<UserForm>();

useFormSubmit()

Access form submission state and submit function.

const { submitting, submitForm } = useFormSubmit();

useFormHistory()

Manage form history for undo/redo functionality.

const { prev, next, canPrev, canNext } = useFormHistory();

useFormArrayField<T>(fieldPath, fallbackValue?)

Manage array fields with specialized array manipulation methods.

const { value, api } = useFormArrayField<string>('tags', []);

// Available methods:
// api.push(item) - Add item to end
// api.remove(index) - Remove item at index
// api.paste(item, index) - Insert item at index
// api.dropLeft(count?) - Remove items from start
// api.dropRight(count?) - Remove items from end
// api.toggle(item) - Toggle item presence

useFormFieldToggle(fieldPath)

Manage boolean toggle fields.

const { value, toggleValue } = useFormFieldToggle('isActive');

useBindFormField<T>(fieldPath, fallBackPath?, fallbackValue?, subscribe?)

Bind a form field with optional fallback path synchronization.

const { value, error, setValue } = useBindFormField<string>(
  'user.name',
  'displayName', // Also updates displayName when user.name changes
  '',
);

FormState Class

For advanced usage, you can access the FormState instance directly:

const formRef = useRef<FormState<UserForm>>(null);

<Form ref={formRef} initialValues={initialValues}>
  {/* form content */}
</Form>;

// Access FormState methods
formRef.current?.getValues();
formRef.current?.setValue('name', 'John');
formRef.current?.validate();

FormState Methods:

  • getValues() - Get all form values
  • setValues(values) - Set all form values
  • getValue<V>(path?) - Get value at specific path
  • setValue<V>(path, value) - Set value at specific path
  • validate() - Validate form using schema
  • subscribeOnChange<V>(callback, path?) - Subscribe to value changes
  • subscribeOnHistoryChange(callback) - Subscribe to history changes
  • prev() - Move to previous history state (undo)
  • next() - Move to next history state (redo)
  • canPrev() - Check if undo is possible
  • canNext() - Check if redo is possible

Utility Functions

objectDiff<T>(obj1, obj2, excludeKeys?)

Compare two objects and return differences.

const diff = objectDiff(oldValues, newValues, ['timestamp']);

isEmpty(obj)

Check if an object is empty.

const isFormEmpty = isEmpty(formValues);

Advanced Examples

Nested Objects

interface UserForm {
  personal: {
    name: string;
    email: string;
  };
  address: {
    street: string;
    city: string;
    country: string;
  };
}

const PersonalInfo = () => {
  const { value, setValue } = useFormField<string>('personal.name', '');
  return <input value={value} onChange={(e) => setValue(e.target.value)} />;
};

const AddressField = () => {
  const { value, setValue } = useFormField<string>('address.street', '');
  return <input value={value} onChange={(e) => setValue(e.target.value)} />;
};

Form History Management

const FormWithHistory = () => (
  <Form
    initialValues={initialValues}
    historyItems={50} // Keep 50 history items
    historyItemThrottle={3} // Save every 3rd change
    filterHistoryKeys={['timestamp']} // Don't track timestamp changes
  >
    <FormFields />
    <HistoryControls />
  </Form>
);

const HistoryControls = () => {
  const { prev, next, canPrev, canNext } = useFormHistory();

  return (
    <div>
      <button onClick={prev} disabled={!canPrev}>
        Undo
      </button>
      <button onClick={next} disabled={!canNext}>
        Redo
      </button>
    </div>
  );
};

Array Field Management

const TagsField = () => {
  const { value, api } = useFormArrayField<string>('tags', []);

  return (
    <div>
      {value.map((tag, index) => (
        <div key={index}>
          <span>{tag}</span>
          <button onClick={() => api.remove(index)}>Remove</button>
        </div>
      ))}
      <input
        onKeyPress={(e) => {
          if (e.key === 'Enter') {
            api.push(e.target.value);
            e.target.value = '';
          }
        }}
        placeholder="Add tag"
      />
    </div>
  );
};

Custom Validation

const customSchema = yup.object({
  password: yup.string().required('Password is required'),
  confirmPassword: yup
    .string()
    .oneOf([yup.ref('password')], 'Passwords must match')
    .required('Confirm password is required'),
});

const PasswordField = () => {
  const { value, error, setValue, touch } = useFormField<string>(
    'password',
    '',
  );

  return (
    <div>
      <input
        type="password"
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onBlur={() => touch('password')}
      />
      {error && <span className="error">{error}</span>}
    </div>
  );
};

TypeScript Support

The library is fully typed with TypeScript. All hooks and components are generic and provide full type safety:

interface MyFormData {
  name: string;
  age: number;
  preferences: {
    theme: 'light' | 'dark';
    notifications: boolean;
  };
}

// Type-safe form
<Form<MyFormData> initialValues={initialValues}>
  {/* Type-safe field access */}
  <MyFormFields />
</Form>;

// Type-safe field hooks
const { value } = useFormField<string>('name'); // value is typed as string
const { value: age } = useFormField<number>('age'); // value is typed as number
const { value: theme } = useFormField<'light' | 'dark'>('preferences.theme');

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.