A form starts with a plain schema object. Keys become field names, and each builder defines the default value, validation, and semantic metadata for that field.
- Web and native can share the same schema.
fieldController(name)is typed from the schema key and value.- The application renders its own inputs and submit button.
stateexposes submission, validity, dirty, error, and value state.
web.tsxtsx
| 1 | import type { FormSchema } from '@runilib/react-formbridge' |
| 2 | import { field, useFormBridge } from '@runilib/react-formbridge' |
| 3 | |
| 4 | const schema = { |
| 5 | fullName: field.text('Full name').required().trim(), |
| 6 | email: field.email('Email').required(), |
| 7 | password: field.password('Password').required().strong(), |
| 8 | terms: field.checkbox('Accept terms').mustBeTrue(), |
| 9 | } satisfies FormSchema |
| 10 | |
| 11 | export function RegistrationForm() { |
| 12 | const form = useFormBridge(schema, { |
| 13 | validateOn: 'onTouched', |
| 14 | onSubmit: save, |
| 15 | }) |
| 16 | const fullName = form.fieldController('fullName') |
| 17 | const email = form.fieldController('email') |
| 18 | const password = form.fieldController('password') |
| 19 | const terms = form.fieldController('terms') |
| 20 | |
| 21 | return ( |
| 22 | <form onSubmit={form.handleSubmit} noValidate> |
| 23 | <input |
| 24 | value={fullName.value} |
| 25 | onChange={(event) => fullName.onChange(event.target.value)} |
| 26 | onBlur={fullName.onBlur} |
| 27 | aria-invalid={Boolean(fullName.error)} |
| 28 | /> |
| 29 | <input |
| 30 | type="email" |
| 31 | value={email.value} |
| 32 | onChange={(event) => email.onChange(event.target.value)} |
| 33 | onBlur={email.onBlur} |
| 34 | /> |
| 35 | <input |
| 36 | type="password" |
| 37 | value={password.value} |
| 38 | onChange={(event) => password.onChange(event.target.value)} |
| 39 | onBlur={password.onBlur} |
| 40 | /> |
| 41 | <label> |
| 42 | <input |
| 43 | type="checkbox" |
| 44 | checked={terms.value} |
| 45 | onChange={(event) => terms.onChange(event.target.checked)} |
| 46 | /> |
| 47 | Accept terms |
| 48 | </label> |
| 49 | <button disabled={form.state.isSubmitting}>Create account</button> |
| 50 | </form> |
| 51 | ) |
| 52 | } |