Quickstart
IMPORTANT
This library is still in early development and the API might change before the first stable release.
This is a quickstart guide to get you started with the Form Signals React bindings. If you want to use a different UI library, check out the other quickstart guides.
Installation
To install the package, run:
npm install @formsignals/form-react @preact/signals-react
INFO
@preact/signals-react
is a peer dependency of @formsignals/form-react
and needs to be installed as well.
WARNING
If you are using Babel or are running into issues with the installation of @preact/signals-react
, you should check out their installation guide here.
If you want to use a schema validation library you can also install the corresponding package.
Creating your first form
This code snippet demonstrates a simple registration form with name
, email
, and password
fields. The example utilizes Zod for validation.
TIP
Just like in this example it is advised to create your own input components, that accept signals as props.
import SignalInput from './SignalInput'
import FieldErrors from './FieldErrors'
import {useForm} from '@formsignals/form-react';
import {ZodAdapter} from "@formsignals/validation-adapter-zod";
import {z} from "zod";
export default function App() {
const form = useForm({
validatorAdapter: ZodAdapter,
defaultValues: {
username: '',
email: '',
password: '',
confirmPassword: '',
},
onSubmit: (values) => {
console.log('Form submitted with values', values)
},
})
return (
<form.FormProvider>
<form
onSubmit={(event) => {
event.preventDefault()
event.stopPropagation()
form.handleSubmit()
}}
>
<form.FieldProvider name="username" validator={z.string().min(3)}>
{(field) => (
<>
<SignalInput
type="text"
placeholder="Username"
name={field.name}
value={field.data}
/>
<FieldErrors/>
</>
)}
</form.FieldProvider>
<form.FieldProvider name="email" validator={z.string().email()}>
{(field) => (
<>
<SignalInput
type="email"
placeholder="Email"
name={field.name}
value={field.data}
/>
<FieldErrors/>
</>
)}
</form.FieldProvider>
<form.FieldProvider name="password" validator={z.string().min(8)}>
{(field) => (
<>
<SignalInput
type="password"
placeholder="Password"
name={field.name}
value={field.data}
/>
<FieldErrors/>
</>
)}
</form.FieldProvider>
<form.FieldProvider
name="confirmPassword"
validator={z
.tuple([z.string(), z.string()])
.refine(
([checkValue, originalValue]) => checkValue === originalValue,
'Passwords do not match',
)}
validateMixin={['password'] as const}
>
{(field) => (
<>
<SignalInput
type="password"
placeholder="Confirm Password"
name={field.name}
value={field.data}
/>
<FieldErrors/>
</>
)}
</form.FieldProvider>
<button type="submit">Submit</button>
</form>
</form.FormProvider>
)
}
import type {Signal} from '@preact/signals-react'
import type {InputHTMLAttributes} from 'react'
interface SignalInputProps
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'> {
value: Signal<string>
}
export default function SignalInput({value, ...props}: SignalInputProps) {
return (
<input
{...props}
value={value.value}
onInput={(event) => {
value.value = event.currentTarget.value
}}
/>
)
}
import {useFieldContext} from '@formsignals/form-react'
export default function FieldErrors() {
const field = useFieldContext()
return (
<div>
{field.errors.value.map((error, index) => (
<div key={index}>{error}</div>
))}
</div>
)
}