Skip to main content

Getting Started

This guide walks you through installing Formora and building your first form with explicit validation rules and predictable UX.

Everything shown here is based on real, tested behavior in Formora.


Installation

npm install formora

Your first form

Below is a minimal, complete example using submit-based validation.

import { useForm } from "formora";

type Values = {
email: string;
password: string;
};

export function LoginForm() {
const { register, values, errors, handleSubmit } = useForm<Values>({
initialValues: {
email: "",
password: "",
},
validateOn: "submit",
});

return (
<form onSubmit={handleSubmit((values) => console.log(values))}>
<div>
<label>Email</label>
<input
{...register("email", {
required: "Email is required",
})}
/>
{errors?.email && <p>{String(errors.email)}</p>}
</div>

<div>
<label>Password</label>
<input
type="password"
{...register("password", {
required: "Password is required",
minLength: { value: 6, message: "Minimum 6 characters" },
})}
/>
{errors?.password && <p>{String(errors.password)}</p>}
</div>

<button type="submit">Sign in</button>

<pre>{JSON.stringify(values, null, 2)}</pre>
</form>
);
}

How validation works here

info

Validation runs only when the form is submitted because validateOn is set to submit.

  • No errors are shown while typing
  • Errors appear after submit
  • In this example we render errors directly from errors

This keeps validation behavior explicit and predictable.


Changing validation behavior

Formora supports three validation modes:

  • submit — validate on submit only (classic forms)
  • change — validate on every change (live feedback)
  • blur — validate when a field loses focus

Switch modes with a single option:

const form = useForm({
validateOn: "blur",
});

Each mode is explained in detail in the Validation Modes section.


Built-in rules vs custom validation

Formora includes built-in rules for common cases. Prefer these when possible because they are short, readable, and consistent.

<input
{...register("password", {
required: "Password is required",
minLength: { value: 6, message: "Minimum 6 characters" },
})}
/>

Custom validate (advanced)

If you need custom logic, use validate. It must return:

  • undefined when the field is valid
  • a string error message when invalid
<input
{...register("password", {
validate: (value, values) => {
const v = String(value ?? "");
if (!v) return "Password is required";
if (v.length < 6) return "Minimum 6 characters";
if (v.includes("password")) return "Too easy";
return undefined;
},
})}
/>

Common patterns

Showing errors

A common pattern is to show errors only after a field is touched:

// Blur/change UX: show errors only after a field is touched
{
touched?.email && errors?.email && <p>{String(errors.email)}</p>;
}

// Submit-only mode: you can also render directly from `errors`
{
errors?.email && <p>{String(errors.email)}</p>;
}

This avoids showing errors too early.


What’s next

Formora scales from simple forms to complex UIs without changing how you reason about validation.