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

@gaddario98/react-form

v2.0.3

Published

[![npm version](https://badge.fury.io/js/@gaddario98%2Freact-form.svg)](https://badge.fury.io/js/@gaddario98%2Freact-form) [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=flat&logo=typescript&logoColor=white)](https://www.typescriptlan

Downloads

49

Readme

@gaddario98/react-form

npm version TypeScript React

An advanced React library for managing dynamic and type-safe forms, built on React Hook Form with full support for TypeScript, internationalization, and notifications.

✨ Features

  • 🎯 Type-Safe: Full TypeScript support with generics and automatic type inference
  • Performance: Advanced optimizations with intelligent memoization and React Compiler
  • 🌍 i18n Ready: Native integration with react-i18next for internationalization
  • 🔔 Notifications: Built-in notification system for success and error handling
  • 🎨 Customizable: Fully customizable containers and components
  • 📱 Responsive: Adaptive layout with header and footer support
  • 🔄 Dynamic: Dynamic forms with conditional and configurable fields
  • 🚀 React Hook Form: Built on the powerful React Hook Form library

📦 Installation

npm install @gaddario98/react-form react-hook-form react-i18next i18next

Peer Dependencies

npm install @gaddario98/react-localization @gaddario98/react-notifications @gaddario98/utiles

🚀 Quick Start

Basic Setup

import { FormManager, setFormConfig } from '@gaddario98/react-form';
import { useForm } from 'react-hook-form';

// Global configuration (optional)
setFormConfig({
  formFieldContainer: ({ children }) => <div className="field-wrapper">{children}</div>,
  errorTranslationOption: { ns: "form-errors" }
});

// Form types
interface UserForm {
  name: string;
  email: string;
  age: number;
}

Basic Example

import React from 'react';
import { FormManager } from '@gaddario98/react-form';
import { useForm } from 'react-hook-form';

const MyForm = () => {
  const formConfig = [
    {
      name: 'name' as const,
      label: 'Name',
      component: ({ value, onChange, error, errorMessage, label }) => (
        <div>
          <label>{label}</label>
          <input
            value={value || ''}
            onChange={(e) => onChange(e.target.value)}
            style={{ borderColor: error ? 'red' : 'initial' }}
          />
          {error && <span style={{ color: 'red' }}>{errorMessage}</span>}
        </div>
      ),
      rules: { required: 'Name is required' }
    },
    {
      name: 'email' as const,
      label: 'Email',
      component: ({ value, onChange, error, errorMessage, label }) => (
        <div>
          <label>{label}</label>
          <input
            type="email"
            value={value || ''}
            onChange={(e) => onChange(e.target.value)}
            style={{ borderColor: error ? 'red' : 'initial' }}
          />
          {error && <span style={{ color: 'red' }}>{errorMessage}</span>}
        </div>
      ),
      rules: { 
        required: 'Email is required',
        pattern: {
          value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
          message: 'Invalid email'
        }
      }
    }
  ];

  const submitButtons = [
    {
      component: ({ onClick }) => (
        <button onClick={onClick} type="submit">
          Save
        </button>
      ),
      onSuccess: (data: UserForm) => {
        console.log('Form submitted:', data);
        alert('Form saved successfully!');
      },
      onError: (error: Error) => {
        console.error('Form error:', error);
      }
    }
  ];

  return (
    <FormManager<UserForm>
      data={formConfig}
      defaultValues={{ name: '', email: '', age: 0 }}
      submit={submitButtons}
      notification={{
        success: { message: 'Data saved successfully!', type: 'success' },
        error: { message: 'Error saving data', type: 'error' }
      }}
    />
  );
};

export default MyForm;

📚 API Reference

FormManager Props

interface FormManagerProps<T extends FieldValues> {
  data: Array<FormManagerConfig<T> | ((props: T) => FormManagerConfig<T>)>;
  defaultValues: DefaultValues<T>;
  onInvalid?: SubmitErrorHandler<T>;
  formControl?: UseFormReturn<T>;
  submit?: Submit<T>[];
  notification?: {
    success?: NotificationConfig | ((res: any) => NotificationConfig);
    error?: NotificationConfig | ((error: string) => NotificationConfig);
    ns?: string;
  };
  container?: React.ComponentType<{ children: React.ReactNode }>;
  onValuesChange?: (props: T) => void;
  formSettings?: Omit<UseFormProps<T>, "defaultValues">;
  ns?: string;
  globalErrorNs?: string;
}

FormManagerConfig

interface FormManagerConfig<T extends FieldValues> {
  name: Path<T>;
  label?: string;
  component: (props: FieldComponentProps<T>) => React.JSX.Element;
  rules?: RegisterOptions<T>;
  container?: React.FC<PropsWithChildren>;
  ns?: string;
  errorNs?: string;
  index?: number;
  renderInFooter?: boolean;
  renderInHeader?: boolean;
  hidden?: boolean;
  helper?: {
    text?: string;
    translationOption?: TOptions;
  };
  onFieldChange?: (value: PathValue<T, Path<T>>) => void;
}

Submit Configuration

interface Submit<T extends FieldValues> {
  onSuccess?: (values: T) => any;
  onError?: (err: Error) => void;
  values?: readonly (keyof T)[];
  component?: (props: {
    onClick: () => void;
    index: number;
    key: string;
    type: "submit";
  }) => React.JSX.Element;
  renderInFooter?: boolean;
  renderInHeader?: boolean;
  hidden?: boolean;
}

🎨 Advanced Examples

Dynamic Form with Conditional Fields

const DynamicForm = () => {
  const dynamicConfig = [
    {
      name: 'userType' as const,
      label: 'User Type',
      component: ({ value, onChange, label }) => (
        <div>
          <label>{label}</label>
          <select value={value || ''} onChange={(e) => onChange(e.target.value)}>
            <option value="">Select...</option>
            <option value="personal">Personal</option>
            <option value="business">Business</option>
          </select>
        </div>
      ),
      rules: { required: 'Please select a user type' }
    },
    // Conditional field that appears only for business users
    (formValues: UserForm) => formValues.userType === 'business' ? {
      name: 'companyName' as const,
      label: 'Company Name',
      component: ({ value, onChange, error, errorMessage, label }) => (
        <div>
          <label>{label}</label>
          <input
            value={value || ''}
            onChange={(e) => onChange(e.target.value)}
            style={{ borderColor: error ? 'red' : 'initial' }}
          />
          {error && <span style={{ color: 'red' }}>{errorMessage}</span>}
        </div>
      ),
      rules: { required: 'Company name is required for business users' }
    } : { name: 'companyName' as const, hidden: true, component: () => null }
  ];

  return (
    <FormManager<UserForm & { userType: string; companyName?: string }>
      data={dynamicConfig}
      defaultValues={{ name: '', email: '', age: 0, userType: '', companyName: '' }}
      submit={[{
        component: ({ onClick }) => <button onClick={onClick}>Submit</button>,
        onSuccess: (data) => console.log('Dynamic form:', data)
      }]}
    />
  );
};

Partial Submit with Specific Validation

const PartialSubmitForm = () => {
  const submitButtons = [
    {
      component: ({ onClick }) => (
        <button onClick={onClick} className="btn-draft">
          Save Draft
        </button>
      ),
      values: ['name'] as const, // Only the name field
      onSuccess: (data: Pick<UserForm, 'name'>) => {
        console.log('Draft saved:', data); // Only { name: string }
      },
      renderInHeader: true
    },
    {
      component: ({ onClick }) => (
        <button onClick={onClick} className="btn-final">
          Submit Final
        </button>
      ),
      // All fields (default)
      onSuccess: (data: UserForm) => {
        console.log('Complete form:', data);
      },
      renderInFooter: true
    }
  ];

  return (
    <FormManager<UserForm>
      data={formConfig}
      defaultValues={{ name: '', email: '', age: 0 }}
      submit={submitButtons}
    />
  );
};

i18n Integration

// i18n setup
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

i18n.use(initReactI18next).init({
  resources: {
    en: {
      forms: {
        name: 'Name',
        email: 'Email',
        age: 'Age'
      },
      errors: {
        required: 'Field required',
        email: 'Invalid email'
      }
    }
  },
  lng: 'en',
  fallbackLng: 'en'
});

// Form with i18n
const InternationalForm = () => {
  const formConfig = [
    {
      name: 'name' as const,
      label: 'name', // Translation key
      component: ({ value, onChange, error, errorMessage, label }) => (
        <div>
          <label>{label}</label> {/* Automatically translated */}
          <input
            value={value || ''}
            onChange={(e) => onChange(e.target.value)}
          />
          {error && <span>{errorMessage}</span>} {/* Translated error */}
        </div>
      ),
      rules: { required: 'required' }, // Translation key
      ns: 'forms' // Namespace for labels
    }
  ];

  return (
    <FormManager<UserForm>
      data={formConfig}
      defaultValues={{ name: '', email: '', age: 0 }}
      ns="forms"
      globalErrorNs="errors"
    />
  );
};

Custom Container and Layout

const CustomLayoutForm = () => {
  const CustomContainer = ({ children }: { children: React.ReactNode }) => (
    <div className="custom-form-container">
      <h2>Custom Form</h2>
      <div className="form-grid">
        {children}
      </div>
    </div>
  );

  const CustomFieldContainer = ({ children }: { children: React.ReactNode }) => (
    <div className="field-group">
      {children}
    </div>
  );

  const formConfig = [
    {
      name: 'name' as const,
      label: 'Name',
      component: ({ value, onChange, label }) => (
        <div className="input-wrapper">
          <label className="fancy-label">{label}</label>
          <input
            className="fancy-input"
            value={value || ''}
            onChange={(e) => onChange(e.target.value)}
          />
        </div>
      ),
      container: CustomFieldContainer,
      index: 1
    }
  ];

  return (
    <FormManager<UserForm>
      data={formConfig}
      defaultValues={{ name: '', email: '', age: 0 }}
      container={CustomContainer}
    />
  );
};

🔧 Configuration

Global Setup

import { setFormConfig } from '@gaddario98/react-form';

setFormConfig({
  formFieldContainer: ({ children }) => (
    <div className="form-field-wrapper">{children}</div>
  ),
  errorTranslationOption: { 
    ns: "form-errors",
    defaultValue: "Validation error"
  }
});

Custom Hook

import { useFormManager } from '@gaddario98/react-form';
import { useForm } from 'react-hook-form';

const useCustomForm = <T extends FieldValues>(config: FormManagerConfig<T>[]) => {
  const formControl = useForm<T>();
  
  const { elements, formContents, errors } = useFormManager({
    data: config,
    formControl,
    submit: [],
    onValuesChange: (values) => {
      console.log('Form values changed:', values);
    }
  });

  return {
    elements,
    formContents,
    errors,
    formControl
  };
};

🎯 Best Practices

1. Correct Typing

// ✅ Define specific types
interface UserForm {
  name: string;
  email: string;
  age: number;
}

// ✅ Use as const for field names
const config = [{
  name: 'name' as const, // Type safety
  // ...
}];

// ❌ Avoid any
const badConfig = [{
  name: 'name', // Type inferred as string
  // ...
}];

2. Memoization

// ✅ Memoize complex configurations
const formConfig = useMemo(() => [
  {
    name: 'name' as const,
    component: MyComponent,
    // ...
  }
], []);

// ✅ Memoize handlers
const handleSuccess = useCallback((data: UserForm) => {
  // Handle success
}, []);

3. Validation

// ✅ Detailed validation
const rules = {
  required: 'Field required',
  minLength: {
    value: 3,
    message: 'Minimum 3 characters'
  },
  validate: {
    noSpaces: (value: string) => 
      !value.includes(' ') || 'Spaces not allowed'
  }
};

🚀 Performance

The library includes several optimizations:

  • Intelligent memoization with custom comparators
  • React Compiler for automatic optimizations
  • Lazy evaluation for conditional fields
  • Reference stability to avoid re-renders
  • Selective updates only for modified fields

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🔗 Useful Links

📞 Support

For support, questions or bug reports, open an issue on GitHub.