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

@sio-group/form-react

v0.2.0

Published

[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC) ![npm](https://img.shields.io/npm/v/@sio-group/form-react) ![TypeScript](https://img.shields.io/badge/types-Yes-brightgreen)

Downloads

286

Readme

@sio-group/form-react

License: ISC npm TypeScript

A powerful, type-safe React form framework. This package provides ready-to-use form components with built-in validation, layout management, and extensive customization options. This package is designed to work seamlessly with @sio-group/form-builder and @sio-group/form-validation, but can be used independently.

Part of the SIO Form ecosystem, it consumes form definitions from @sio-group/form-builder and renders them with full type safety and accessibility in mind.


Installation

npm install @sio-group/form-react

Peer Dependencies:

  • react: ^19.0.0
  • react-dom: ^19.0.0

Quick Example

import { Form } from '@sio-group/form-react';
import { formBuilder } from '@sio-group/form-builder';

function ContactForm() {
  const fields = formBuilder()
    .addText('name', {
      label: 'Full name',
      required: true,
      placeholder: 'John Doe'
    })
    .addEmail('email', {
      label: 'Email address',
      required: true,
      placeholder: '[email protected]'
    })
    .addTextarea('message', {
      label: 'Message',
      rows: 5,
      placeholder: 'Your message...'
    })
    .getFields();

  return (
    <Form
      fields={fields}
      submitAction={(values) => console.log('Form submitted:', values)}
      submitLabel="Send Message"
    />
  );
}

Voorbeeld

Contact form A simple contact form


Features

  • Type-safe - Full TypeScript support with inferred types
  • Built-in validation - Automatic validation based on field configuration
  • Responsive layout - Grid system with breakpoints (sm, md, lg)
  • Customizable buttons - Multiple button variants and colors
  • Form state management - Tracks dirty, touched, focused, and error states
  • Accessibility - ARIA attributes and keyboard navigation
  • Offline support - Disable forms when offline
  • File uploads - Built-in file handling with validation
  • Icons - Support for HTML and component icons
  • Flexible containers - Custom form and button containers

Core Components

Form Component

The main component that renders your form with all fields and buttons.

import { Form } from '@sio-group/form-react';

<Form
  fields={fields}
  layout={layoutConfig}
  submitShow={true}
  submitAction={handleSubmit}
  submitLabel="Save"
  cancelShow={true}
  cancelAction={handleCancel}
  cancelLabel="Cancel"
  buttons={buttonConfig}
  extraValidation={customGlobalValidation}
  className="my-form"
  container={CustomContainer}
  buttonContainer={CustomButtonContainer}
/>

Individual Field Components

You can also use field components independently:

import { 
  useForm,
  Input, 
  Textarea, 
  Select, 
  Radio, 
  Checkbox,
  NumberInput,
  RangeInput,
  DateInput,
  FileInput,
  TextInput 
} from '@sio-group/form-react';

// Use with useForm hook
const { register } = useForm();

return (
  <Input {...register('username', fieldConfig)} />
);

API Reference

Form Props

| Prop | Type | Default | Description | |----------------------|--------------------------------|--------------------------|------------------------------| | fields | FormField[] | (required) | Array of form fields | | submitAction | (values: any) => void | (required) | Submit handler | | layout | FormLayout[] | [] | Custom layout configuration | | submitShow | boolean | true | Show submit button | | submitLabel | string | 'Bewaar' | Submit button text | | cancelShow | boolean | false | Show cancel button | | cancelLabel | string | 'Annuleren' | Cancel button text | | cancelAction | () => void | - | Cancel handler | | buttons | (ButtonProps \| LinkProps)[] | [] | Additional buttons | | extraValidation | (values: any) => boolean | () => true | Extra validation | | className | string | - | CSS class for form container | | style | React.CSSProperties | - | Inline styles | | disableWhenOffline | boolean | true | Disable form when offline | | container | React.ComponentType | DefaultContainer | Custom form container | | buttonContainer | React.ComponentType | DefaultButtonContainer | Custom button container |

Layout Configuration

The layout prop allows you to arrange fields in a responsive grid:

const layout = [
  { 
    layout: { sm: 12, md: 6, lg: 4 },  // Responsive column spans
    fields: ['name', 'email']           // Fields in this row
  },
  {
    layout: { sm: 12, md: 12, lg: 8 },
    fields: ['message'],
    className: 'custom-row',            // Optional CSS class
    style: { marginBottom: '2rem' }      // Optional inline styles
  }
];

Breakpoint options:

  • sm: Small (≥640px)
  • md: Medium (≥768px)
  • lg: Large (≥1024px)

Button Configuration

Buttons can be configured as an array:

const buttons = [
  {
    type: 'button',
    variant: 'primary',
    color: 'success',
    label: 'Save Draft',
    onClick: () => saveDraft()
  },
  {
    type: 'button',
    variant: 'secondary',
    color: 'warning',
    label: 'Reset',
    onClick: () => reset()
  },
  {
    type: 'link',
    variant: 'link',
    color: 'info',
    label: 'Help',
    href: '/help'
  }
];

Button Props:

  • type: 'button' | 'link'
  • variant: 'primary' | 'secondary' | 'link'
  • color: 'default' | 'success' | 'warning' | 'error' | 'info'
  • label: string
  • onClick?: () => void (for buttons)
  • href?: string (for links)
  • loading?: boolean
  • disabled?: boolean

Hooks

useForm

A powerful hook for managing form state independently:

import { useForm } from '@sio-group/form-react';

function CustomForm() {
  const { register, getValues, isValid, isBusy, reset, submit } = useForm();

  const handleSubmit = async () => {
    await submit(async (values) => {
      await api.save(values);
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <Input {...register('username', fieldConfig)} />
      <button type="submit" disabled={!isValid() || isBusy()}>
        Submit
      </button>
    </form>
  );
}

useForm Return Value:

| Method | Description | |--------------------------|--------------------------------| | register(name, config) | Register a field and get props | | unregister(name) | Remove a field from form | | setValue(name, value) | Set field value | | getValues() | Get all form values | | getValue(name) | Get single field value | | reset() | Reset form state | | isValid() | Check if form is valid | | isDirty() | Check if form has changes | | isBusy() | Check if form is submitting | | submit(handler) | Submit form with handler | | getField(name) | Get field state |

Conditional Fields

You can dynamically render fields based on other field values.

import { useForm, Input, Checkbox } from '@sio-group/form-react';

function Example() {
  const { register, getValue } = useForm();
  
  return (
    <>
      <Checkbox
        {...register('subscribe', { 
          name: 'subscribe', 
          type: 'checkbox', 
          config: { label: 'Subscribe to newsletter' } 
        })} 
      />

      {getValue('subscribe') && (
        <Input
          {...register('email', {
            name: 'email',
            type: 'email',
            config: {
              label: 'Email address',
              required: true
            }
          })}
        />
      )}
    </>
  );
}

Custom Validation

Default validation is automatically derived from the field configuration (required, min, max, email, etc.). Additional validation rules can be added using the validations array.

import { Input } from '@sio-group/form-react';

<Input
  {...register('username', {
    name: 'username',
    type: 'text',
    config: {
      label: 'Username',
      required: true,
      validations: [
        (value) => value.length < 3 ? 'Username must contain at least 3 characters' : null,
        (value) => value.includes('admin') ? 'Username cannot contain admin' : null
      ]
    },
  })}
/>

Username Custom validation for admin in username


Field Components

Input Types

All standard HTML input types are supported:

  • text, search, email, tel, password, url
  • number (with spinner options)
  • range (with value display)
  • date, time, datetime-local
  • color
  • hidden
  • file (with file validation)

Specialized Components

NumberInput

<NumberInput
  {...register('age', {
    name: "age",
    type: "number",
    config: {
      label: "Age",
      min: 0,
      max: 120,
      step: 1,
      spinner: 'horizontal' // or "vertical", true, false
    }
  }) as NumberFieldProps}
/>

NumberInput Number input with horizontal spinner

RangeInput

<RangeInput
  {...register('volume', {
    name: "volume",
    type: "range",
    config: {
      label: "Volume",
      min: 0,
      max: 120,
      step: 1,
      showValue: true,
    }
  }) as NumberFieldProps}
/>

RangeInput Range input with shown value

FileInput

<FileInput
  {...register('documents', {
    name: "documents",
    type: "file",
    config: {
      label: "Documenten",
      accept: ".pdf,.doc",
      multiple: true,
      filesize: 5120, // 5MB in KB
      capture: false,
      onFileRemove: (file) => console.log('Removed:', file),
      onRemoveAll: (files) => console.log('All removed:', files)
    }
  }) as FileFieldProps}
/>

FileInput Multiple file input

Select

<Select
  {...register('country', {
    name: 'country',
    type: 'select',
    config: {
      options: [
        { value: 'be', label: 'Belgium' },
        { value: 'nl', label: 'Netherlands' },
        {
          label: 'Europe',
          options: [
            { value: 'fr', label: 'France' }
          ]
        }
      ],
      multiple: false,
      placeholder: "Choose a country"
    }
  }) as SelectFieldProps}
/>

Select Single select input

Radio

<Radio
  {...register('country', {
    name: "country",
    type: "radio",
    config: {
      label: "Country",
      options: ['Red', 'Green', 'Blue'],
      inline: true
    }
  }) as RadioFieldProps}
/>

Radio Inline radio input

Textarea

<Textarea
  {...register('bio', {
    name: "bio",
    type: "textarea",
    config: {
      label: "Bio",
      placeholder: "Tell us about yourself...",
      rows: 5,
      cols: 40,
    }
  }) as TextareaFieldProps}
/>

Textarea Textarea input


Standalone Components

Controlled Usage (with useForm)

You can use the useForm hook to control how you use the form, centrally managing state, validation, and submission. This also demonstrates conditional rendering and error automation.

import { useForm, Input, Button } from '@sio-group/form-react';

function FormWithHook() {
  const { register, getValue, isValid, isBusy, submit } = useForm();

  const handleSubmit = async (e) => {
    e.preventDefault();
    await submit(values => console.log('Form values:', values));
  };

  return (
    <form noValidate>
      <Input
        {...register('email', {
          name: 'email',
          type: 'email',
          config: {
            label: 'Email Address',
            required: true,
            validations: [
              val => val.includes('@') ? null : 'Must be a valid email',
            ]
          }
        })}
      />

      <Button
        type="submit"
        variant="primary"
        disabled={!isValid()}
        onClick={handleSubmit}
      >
        {isBusy() ? 'sending' : 'Submit'}
      </Button>

      {getValue('email') && <p>Your email: {getValue('email')}</p>}
    </form>
  );
}

Uncontrolled Usage (without useForm)

All field components and buttons can also be used independently. You can keep and manage state for the form depending on your needs.

import { useState } from 'react';
import { Input, Button } from '@sio-group/form-react';

function SimpleForm() {
  const [value, setValue] = useState('');
  const [error, setError] = useState('');
  const [touched, setTouched] = useState(false);
  const [focused, setFocused] = useState(false);
  const [isValid, setIsValid] = useState(false);

  const handleChange = (value) => {
    if (!value) {
      setError("This is wrong");
      setIsValid(false);
    } else {
      setError("");
      setIsValid(true);
    }

    setValue(value);
  }

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(value);
  }

  return (
    <form noValidate>
      <TextInput
        type="email"
        id="email"
        name="email"
        value={value}
        errors={error ? [error] : []}
        touched={touched}
        focused={focused}
        disabled={false}
        onChange={handleChange}
        setFocused={setFocused}
        setTouched={setTouched}
      />

      <Button
        type="submit"
        variant="primary"
        onClick={handleSubmit}
        disabled={!isValid}
      >
        Submit
      </Button>
    </form>
  );
}

Button Props

| Prop | Type | Default | Description | |------------|------------------------------------------------------------|-------------|-------------------------| | type | 'button' \| 'submit' \| 'reset' | 'button' | Button type | | label | string | - | Label of the Link | | onClick | (e: React.MouseEvent) => void | - | Custom onClick function | | variant | 'primary' \| 'secondary' \| 'link' | 'primary' | Visual variant | | color | 'default' \| 'error' \| 'success' \| 'warning' \| 'info' | 'default' | Color theme | | size | 'sm' \| 'md' \| 'lg' | 'md' | Button size | | block | boolean | false | Full width | | loading | boolean | false | Loading state | | disabled | boolean | false | Disabled state |

Link Props

| Prop | Type | Default | Description | |------------|------------------------------------------------------------|-------------|--------------------------| | label | string | - | Label of the Link | | to | string | '#' | URL or pad | | onClick | (e: React.MouseEvent) => void | - | Custom onClick function | | color | 'default' \| 'error' \| 'success' \| 'warning' \| 'info' | 'default' | Color theme | | size | 'sm' \| 'md' \| 'lg' | 'md' | Button size | | block | boolean | false | Full width | | loading | boolean | false | Loading state | | disabled | boolean | false | Disabled state | | navigate | () => void | - | Custom navigate function | | external | boolean | false | Force external link |


Styling

Default Styles

Import the default styles:

import '@sio-group/form-react/sio-form-style.css';

Custom Styling

Each component accepts className and style props for custom styling:

<Input
  {...register('username', {
    name: 'username',
    type: 'text',
    config: {
      styling: {
        className: 'custom-input',
        style: {
          backgroundColor: '#f0f0f0'
        }
      }
    }
  })}
/>

Layout Classes

The form uses a responsive grid system. You can target these classes:

  • .sio-row - Grid container
  • .sio-col-xs-* - Column classes for breakpoints
  • .sio-col-sm-*
  • .sio-col-md-*
  • .sio-col-lg-*
  • .sio-col-xl-*

Example with Tailwind CSS:

<Form
  fields={fields}
  className="max-w-2xl mx-auto"
  layout={[
    { layout: { md: 6 }, fields: ['firstName'], className: 'pr-2' },
    { layout: { md: 6 }, fields: ['lastName'], className: 'pl-2' }
  ]}
/>

Complete Example

import { Form } from '@sio-group/form-react';
import { formBuilder } from '@sio-group/form-builder';
import '@sio-group/form-react/sio-form-style.css';

function RegistrationForm() {
  const fields = formBuilder()
    .addText('firstName', {
      label: 'First name',
      required: true,
      layout: { md: 6 }
    })
    .addText('lastName', {
      label: 'Last name',
      required: true,
      layout: { md: 6 }
    })
    .addEmail('email', {
      label: 'Email',
      required: true,
      layout: { md: 6 }
    })
    .addTelephone('phone', {
      label: 'Phone',
      layout: { md: 6 }
    })
    .addPassword('password', {
      label: 'Password',
      required: true,
      layout: { md: 6 }
    })
    .addPassword('confirmPassword', {
      label: 'Confirm password',
      required: true,
      layout: { md: 6 }
    })
    .addCheckbox('terms', {
      label: 'I accept the terms and conditions',
      required: true
    })
    .getFields();

  const handleSubmit = (values) => {
    console.log('Registration:', values);
  };

  const customButtons = [
    {
      type: 'button',
      variant: 'secondary',
      color: 'info',
      label: 'Clear form',
      onClick: () => console.log('Clear')
    }
  ];

  return (
    <Form
      fields={fields}
      submitAction={handleSubmit}
      submitLabel="Register"
      cancelShow={true}
      cancelAction={() => console.log('Cancelled')}
      buttons={customButtons}
      className="registration-form"
      layout={[
        { layout: { md: 6 }, fields: ['firstName'] },
        { layout: { md: 6 }, fields: ['lastName'] },
        { layout: { md: 6 }, fields: ['email'] },
        { layout: { md: 6 }, fields: ['phone'] },
        { layout: { md: 6 }, fields: ['password'] },
        { layout: { md: 6 }, fields: ['confirmPassword'] },
        { layout: { md: 12 }, fields: ['terms'] }
      ]}
    />
  );
}

RegistrationForm A simple registration form with simple layout


Ecosystem

@sio-group/form-react is part of the SIO Form ecosystem:


Contributing

Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.

License

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