Validation
Validate form inputs with built-in and custom functions.
Built-in Validators
json-render includes common validation functions:
required— Value must be non-emptyemail— Valid email formatminLength— Minimum string lengthmaxLength— Maximum string lengthpattern— Match a regex patternmin— Minimum numeric valuemax— Maximum numeric value
Using Validation in JSON
Use { "$bindState": "/path" } on the value prop for two-way binding. Validation checks run against the value at the bound path (available as bindings?.value in components):
{
"type": "TextField",
"props": {
"label": "Email",
"value": { "$bindState": "/form/email" },
"checks": [
{ "type": "required", "message": "Email is required" },
{ "type": "email", "message": "Invalid email format" }
],
"validateOn": "blur"
}
}Validation with Parameters
{
"type": "TextField",
"props": {
"label": "Password",
"value": { "$bindState": "/form/password" },
"checks": [
{ "type": "required", "message": "Password is required" },
{
"type": "minLength",
"args": { "min": 8 },
"message": "Password must be at least 8 characters"
},
{
"type": "pattern",
"args": { "pattern": "[A-Z]" },
"message": "Must contain at least one uppercase letter"
}
]
}
}Custom Validation Functions
Define custom validators in your catalog's functions field. The catalog itself is framework-agnostic — only the schema import varies by platform:
import { defineCatalog } from '@json-render/core';
import { schema } from '@json-render/react'; // or '@json-render/react-native'
import { z } from 'zod';
const catalog = defineCatalog(schema, {
components: { /* ... */ },
functions: {
isValidPhone: {
description: 'Validates phone number format',
},
isUniqueEmail: {
description: 'Checks if email is not already registered',
},
},
});Usage with React
In @json-render/react, use ValidationProvider to supply implementations for your custom validators:
import { ValidationProvider } from '@json-render/react';
function App() {
const customValidators = {
isValidPhone: (value) => {
const phoneRegex = /^\+?[1-9]\d{1,14}$/;
return phoneRegex.test(value);
},
isUniqueEmail: async (value) => {
const response = await fetch(`/api/check-email?email=${value}`);
const { available } = await response.json();
return available;
},
};
return (
<ValidationProvider customFunctions={customValidators}>
{/* Your UI */}
</ValidationProvider>
);
}Using in Components
The useFieldValidation and useBoundProp hooks wire validation into your registry components. Validation uses the path from bindings?.value (the bound state path):
import { useFieldValidation, useBoundProp } from '@json-render/react';
function TextField({ props, bindings }) {
const [value, setValue] = useBoundProp(props.value, bindings?.value);
const { errors, isValid, validate, touch, clear } = useFieldValidation(
bindings?.value ?? null,
{ checks: props.checks, validateOn: props.validateOn }
);
return (
<div>
<label>{props.label}</label>
<input
value={value || ''}
onChange={(e) => setValue(e.target.value)}
onBlur={() => validate()}
/>
{errors.map((error, i) => (
<p key={i} className="text-red-500 text-sm">{error}</p>
))}
</div>
);
}See the @json-render/react API reference for full ValidationProvider and useFieldValidation documentation.
Validation Timing
Control when validation runs with validateOn:
change— Validate on every input changeblur— Validate when field loses focussubmit— Validate only on form submission
Next
Learn about generation modes.