Ultra-high-performance React form library with field-level reactivity and tree-shakable architecture
npm install @page-speed/formsType-safe form state management and validation for React applications.
OpenSite Page Speed Forms is a high-performance library designed to streamline form state management, validation, and submission handling in React applications. This library is part of OpenSite AI's open-source ecosystem, built for performance and open collaboration. By emphasizing type safety and modularity, it aligns with OpenSite's goal to create scalable, open, and developer-friendly performance tooling.





Learn more at OpenSite.ai Developers.
- Type-safe form state management with TypeScript.
- Flexible validation schemas supporting both synchronous and asynchronous validation.
- Modular useForm and useField hooks for complete form and field control.
- Built-in support for form submission and error handling.
- Configurable validation modes: onChange, onBlur, and onSubmit.
To install OpenSite Page Speed Forms, ensure you have Node.js and npm installed, then run:
```
npm install @page-speed/forms
Dependencies:
- React
Here is a basic example to get started with OpenSite Page Speed Forms in your React application:
`typescript
import React from 'react';
import { useForm, Form } from '@page-speed/forms';
function MyForm() {
const form = useForm({
initialValues: { email: '' },
onSubmit: (values) => {
console.log('Form Submitted:', values);
}
});
return (
Configuration or Advanced Usage
OpenSite Page Speed Forms can be customized with various options:
`typescript
const form = useForm({
initialValues: { email: '' },
validationSchema: {
email: (value) => value.includes('@') ? undefined : 'Invalid email'
},
validateOn: 'onBlur',
revalidateOn: 'onChange',
onSubmit: (values) => console.log(values),
onError: (errors) => console.error(errors),
debug: true
});
`Advanced Validation Features
$3
Validate fields that depend on other field values using the
crossFieldValidator utility or by accessing allValues in your validator:`typescript
import { useForm, crossFieldValidator } from '@page-speed/forms/validation';// Method 1: Using crossFieldValidator helper
const form = useForm({
initialValues: { password: '', confirmPassword: '' },
validationSchema: {
confirmPassword: crossFieldValidator(
['password', 'confirmPassword'],
(values) => {
if (values.password !== values.confirmPassword) {
return 'Passwords must match';
}
return undefined;
}
)
}
});
// Method 2: Direct access to allValues
const form = useForm({
initialValues: { password: '', confirmPassword: '' },
validationSchema: {
confirmPassword: (value, allValues) => {
if (value !== allValues.password) {
return 'Passwords must match';
}
return undefined;
}
}
});
`$3
Optimize async validators (like API calls) with built-in debouncing to prevent excessive requests:
`typescript
import { useForm, asyncValidator } from '@page-speed/forms/validation';const checkUsernameAvailability = async (username: string) => {
const response = await fetch(
/api/check-username?username=${username});
const { available } = await response.json();
return available ? undefined : 'Username already taken';
};const form = useForm({
initialValues: { username: '' },
validationSchema: {
// Debounce async validation by 500ms
username: asyncValidator(
checkUsernameAvailability,
{ delay: 500, trailing: true }
)
}
});
`Debounce Options:
-
delay: Milliseconds to wait (default: 300ms)
- leading: Validate immediately on first change (default: false)
- trailing: Validate after delay expires (default: true)The
asyncValidator wrapper also includes automatic race condition prevention, ensuring only the latest validation result is used.$3
Use pre-built, tree-shakable validation rules for common scenarios:
`typescript
import {
required,
email,
url,
phone,
minLength,
maxLength,
min,
max,
pattern,
matches,
oneOf,
creditCard,
postalCode,
alpha,
alphanumeric,
numeric,
integer,
compose
} from '@page-speed/forms/validation/rules';const form = useForm({
initialValues: {
email: '',
password: '',
confirmPassword: '',
age: 0,
username: '',
cardNumber: ''
},
validationSchema: {
email: compose(
required({ message: 'Email is required' }),
email({ message: 'Invalid email format' })
),
password: compose(
required(),
minLength(8, { message: 'Password must be at least 8 characters' })
),
confirmPassword: matches('password', { message: 'Passwords must match' }),
age: compose(
required(),
numeric({ message: 'Age must be a number' }),
min(18, { message: 'Must be 18 or older' })
),
username: compose(
required(),
alphanumeric({ message: 'Only letters and numbers allowed' }),
minLength(3),
maxLength(20)
),
cardNumber: creditCard({ message: 'Invalid credit card number' })
}
});
`Available Validators:
| Validator | Description | Example |
|-----------|-------------|---------|
|
required() | Field must have a value | required({ message: 'Required' }) |
| email() | Valid email format (RFC 5322) | email() |
| url() | Valid URL format | url() |
| phone() | US phone number format | phone() |
| minLength(n) | Minimum string/array length | minLength(3) |
| maxLength(n) | Maximum string/array length | maxLength(100) |
| min(n) | Minimum numeric value | min(0) |
| max(n) | Maximum numeric value | max(100) |
| pattern(regex) | Custom regex pattern | pattern(/^[A-Z]+$/) |
| matches(field) | Match another field | matches('password') |
| oneOf(values) | Value in allowed list | oneOf(['a', 'b', 'c']) |
| creditCard() | Valid credit card (Luhn) | creditCard() |
| postalCode() | US ZIP code format | postalCode() |
| alpha() | Alphabetic characters only | alpha() |
| alphanumeric() | Letters and numbers only | alphanumeric() |
| numeric() | Valid number | numeric() |
| integer() | Whole number | integer() |
| compose(...) | Combine multiple validators | compose(required(), email()) |$3
Customize error messages globally for internationalization support:
`typescript
import { setErrorMessages } from '@page-speed/forms/validation/utils';// Set custom messages (e.g., Spanish translations)
setErrorMessages({
required: 'Este campo es obligatorio',
email: 'Por favor ingrese un correo electrónico válido',
minLength: ({ min }) =>
Debe tener al menos ${min} caracteres,
maxLength: ({ max }) => No debe exceder ${max} caracteres,
phone: 'Por favor ingrese un número de teléfono válido'
});// Use with validation rules
import { required, email, minLength } from '@page-speed/forms/validation/rules';
const form = useForm({
initialValues: { email: '', password: '' },
validationSchema: {
email: compose(required(), email()),
password: compose(required(), minLength(8))
}
});
`Message Template Functions:
Error messages support template functions with parameter interpolation:
`typescript
setErrorMessages({
minLength: ({ min }) => Must be at least ${min} characters,
max: ({ max }) => Cannot exceed ${max},
matches: ({ field }) => Must match ${field}
});
`Per-Field Custom Messages:
Override global messages on a per-field basis:
`typescript
const form = useForm({
initialValues: { email: '' },
validationSchema: {
email: required({ message: 'Please provide your email address' })
}
});
`$3
Validate fields only when certain conditions are met:
`typescript
import { when, required, minLength } from '@page-speed/forms/validation';const form = useForm({
initialValues: { accountType: 'personal', companyName: '' },
validationSchema: {
// Only require company name for business accounts
companyName: when(
(allValues) => allValues.accountType === 'business',
compose(
required({ message: 'Company name is required for business accounts' }),
minLength(3)
)
)
}
});
`Built-in Input Components
@page-speed/forms includes a comprehensive set of accessible, production-ready input components that work seamlessly with the form hooks.$3
#### TextInput
Standard text input with support for various types (text, email, password, etc.):
`typescript
import { TextInput } from '@page-speed/forms/inputs';
{({ field }) => }
`#### TextArea
Multi-line text input:
`typescript
import { TextArea } from '@page-speed/forms/inputs';
{({ field }) => }
`#### Checkbox & CheckboxGroup
Single checkbox or group of checkboxes:
`typescript
import { Checkbox, CheckboxGroup } from '@page-speed/forms/inputs';// Single checkbox
{({ field }) => }
// Checkbox group
{({ field }) => (
{...field}
options={[
{ label: 'Sports', value: 'sports' },
{ label: 'Music', value: 'music' },
{ label: 'Travel', value: 'travel' }
]}
/>
)}
`#### Radio
Radio button group:
`typescript
import { Radio } from '@page-speed/forms/inputs';
{({ field }) => (
{...field}
options={[
{ label: 'Basic', value: 'basic' },
{ label: 'Pro', value: 'pro' },
{ label: 'Enterprise', value: 'enterprise' }
]}
/>
)}
`#### Select
Dropdown select with support for single and multi-select:
`typescript
import { Select } from '@page-speed/forms/inputs';// Single select
{({ field }) => (
{...field}
options={[
{ label: 'United States', value: 'us' },
{ label: 'Canada', value: 'ca' },
{ label: 'United Kingdom', value: 'uk' }
]}
searchable
clearable
/>
)}
// Multi-select
{({ field }) => (
{...field}
multiple
options={[
{ label: 'JavaScript', value: 'js' },
{ label: 'TypeScript', value: 'ts' },
{ label: 'React', value: 'react' }
]}
searchable
/>
)}
`$3
#### DatePicker
Date selection with calendar popup:
`typescript
import { DatePicker } from '@page-speed/forms/inputs';
{({ field }) => (
{...field}
placeholder="Select date"
dateFormat="MM/dd/yyyy"
minDate={new Date(1900, 0, 1)}
maxDate={new Date()}
clearable
/>
)}
`Props:
-
dateFormat: Date display format (default: "MM/dd/yyyy")
- minDate, maxDate: Restrict selectable dates
- isDateDisabled: Custom function to disable specific dates
- clearable: Show clear button
- showTodayButton: Show "Today" button#### TimePicker
Time selection with hour/minute/period selectors:
`typescript
import { TimePicker } from '@page-speed/forms/inputs';
{({ field }) => (
{...field}
placeholder="Select time"
use24Hour={false}
minuteStep={15}
clearable
/>
)}
`Props:
-
use24Hour: Use 24-hour format (default: false)
- minuteStep: Minute increment (default: 1)
- clearable: Show clear button#### DateRangePicker
Date range selection with start and end dates:
`typescript
import { DateRangePicker } from '@page-speed/forms/inputs';
{({ field }) => (
{...field}
placeholder="Select date range"
separator=" - "
minDate={new Date()}
clearable
/>
)}
`Props:
-
separator: String between start and end dates (default: " - ")
- minDate, maxDate: Restrict selectable dates
- isDateDisabled: Custom function to disable specific dates
- clearable: Show clear button#### RichTextEditor
WYSIWYG and Markdown editor with toolbar:
`typescript
import { RichTextEditor } from '@page-speed/forms/inputs';
{({ field }) => (
{...field}
placeholder="Enter content..."
minHeight="200px"
maxHeight="600px"
allowModeSwitch
defaultMode="wysiwyg"
/>
)}
`Props:
-
defaultMode: "wysiwyg" or "markdown" (default: "wysiwyg")
- allowModeSwitch: Enable mode toggle button
- minHeight, maxHeight: Editor height constraints
- customButtons: Add custom toolbar buttonsFeatures:
- WYSIWYG mode: Bold, Italic, Underline, Headings, Lists, Links
- Markdown mode: Direct markdown editing
- Automatic HTML ↔ Markdown conversion
#### FileInput
File upload with drag-and-drop, progress indicators, and image cropping:
`typescript
import { FileInput } from '@page-speed/forms/inputs';
{({ field }) => (
{...field}
accept="image/*"
maxSize={5 1024 1024} // 5MB
maxFiles={1}
showProgress
uploadProgress={uploadProgress}
enableCropping
cropAspectRatio={1}
onCropComplete={(file) => console.log('Cropped:', file)}
/>
)}
`Props:
-
accept: File type filter (e.g., "image/*", ".pdf")
- multiple: Allow multiple files
- maxFiles: Maximum number of files
- maxSize: Maximum file size in bytes
- showPreview: Show file previews
- showProgress: Display upload progress bars
- uploadProgress: Object mapping filenames to progress percentages
- enableCropping: Enable image cropping for image files
- cropAspectRatio: Crop aspect ratio (e.g., 16/9, 1 for square)
- onCropComplete: Callback when cropping is completeFeatures:
- Drag-and-drop support
- File type and size validation
- Image previews with thumbnails
- Upload progress indicators with percentage
- Interactive image cropping with zoom
- Multiple file support
- Accessible file selection
$3
The
FileInput component uses a two-phase upload process optimized for Rails API integration. Files are uploaded immediately to temporary storage and return unique tokens, which are then associated with your form submission.Quick Example:
`tsx
const [uploadTokens, setUploadTokens] = useState([]);const handleFileUpload = async (files: File[]) => {
const formData = new FormData();
formData.append("contact_form_upload[file_upload]", files[0]);
const response = await fetch("/api/contact_form_uploads", {
method: "POST",
body: formData,
});
const data = await response.json();
setUploadTokens([data.token]);
};
// In your form submission:
onSubmit: async (values) => {
await submitForm({
...values,
contact_form_upload_tokens: uploadTokens,
});
}
`Comprehensive Guide:
For complete file upload documentation, including:
- Two-phase upload process and flow diagrams
- Rails API integration with endpoint specifications
- Multiple working examples (resume uploads, image galleries, document forms)
- Progress tracking and error handling patterns
- Image cropping implementation
- File validation strategies
- Best practices and common patterns
- Troubleshooting guide
See the File Upload Guide for detailed information.
Styling
All components in
@page-speed/forms are intentionally unstyled to provide maximum flexibility and framework-agnostic design. Components use predictable BEM class names (e.g., .text-input, .select-trigger, .field-label) as styling hooks, allowing you to apply any design system or custom styles.Quick Example:
`css
/ Your custom CSS /
.text-input {
height: 2.25rem;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
padding: 0.5rem 0.75rem;
}.text-input:focus {
outline: none;
border-color: #3b82f6;
}
.text-input--error {
border-color: #ef4444;
}
``Comprehensive Guide:
For complete styling documentation, including:
- BEM class reference for all components
- Multiple styling approaches (Vanilla CSS, Tailwind, CSS Modules, CSS-in-JS)
- Complete examples (shadcn/ui, Material Design, custom brands)
- Best practices and common patterns
- Dark mode support
See the Styling Guide for detailed information.
Performance is a core facet of everything we build at OpenSite AI. The library is optimized for minimal re-renders and efficient form state updates, ensuring your applications remain responsive and fast.
We welcome contributions from the community to enhance OpenSite Page Speed Forms. Please refer to our GitHub repository for guidelines and more information on how to get involved.
Licensed under the BSD 3-Clause License. See the LICENSE file for details.
- Domain Extractor
- Page Speed Hooks
- Visit opensite.ai for more tools and information.