Docs

Validation

Validate Niral form actions and RPC inputs with built-in schemas. Coerce form values, validate nested data and return useful field-level error messages.

Built-in coercing schemas — form strings become typed values, errors become field messages. No Zod needed.

export const signup = withSchema(
  {
    email: v.email(),
    age: v.int({ min: 13 }),
    tags: v.array(v.string({ min: 1 }), { min: 1, max: 5 }),
    bio: v.optional(v.string({ max: 500 })),
  },
  async ({ email, age, tags, bio }) => {
    // values are validated AND coerced ("42" → 42, "on" → true)
    return { ok: true }
  }
)
  • RPC callers get a 400 with { errors: { field: message } } — the thrown

client error carries .errors for form UIs

  • Ordinary form actions surface it as form.errors.field in props
  • Reactive form actions expose saving.errors.field on the

formAction("signup") controller without remounting the form

  • v.file() validates uploads (size, types — image/* wildcards work)

Rules: string, email, int, number, bool, oneOf, array, file, object, optional.

TypeScript infers withSchema handler values and return types, as well as validate(shape, data) results. Check result.ok before using its fully validated value. Nested object errors retain their field structure; array validation errors are a single message for the field.