Fields from a JSON Schema
fieldsFromJsonSchema turns a JSON Schema object into a field list, so a form can be driven by the schema an API already publishes (an OpenAPI components.schemas entry, for example).
import { fieldsFromJsonSchema } from '@dynamic-field-kit/core';
const { fields, defaults, warnings } = fieldsFromJsonSchema(
{
type: 'object',
required: ['email'],
properties: {
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 18 },
plan: { enum: ['free', 'pro'], default: 'free' },
contacts: {
type: 'array',
items: {
type: 'object',
properties: { phone: { type: 'string' } },
},
},
},
},
{ overrides: { 'contacts[].phone': { placeholder: '+84…' } } },
);fieldsgoes toMultiFieldInput/useDynamicFormlike a hand-written list.defaultsholds the schema'sdefaultvalues, shaped like the form data. Pass it as the initial data.warningslists every property or keyword that did not make it into the form, with its path. Nothing is dropped silently.
| Schema | Field |
|---|---|
string | text; format picks email, date, time, datetime-local (date-time) or password |
number, integer | number, with min / max from minimum / maximum and step from multipleOf (1 for an integer) |
boolean | checkbox |
enum, or oneOf / anyOf made only of const | select; a const member's title is the option label |
| array of enums | select with multiple |
| array of objects | repeatable group, with minItems / maxItems and a defaultItem from the item defaults |
| nested object | section, with the defaults of its properties as the section's default |
title becomes the label (a readable form of the property name otherwise), description the description, readOnly a readOnlyCondition. required, minLength, maxLength, pattern, minimum, maximum and the email format become validate hooks built from validators, so their messages go through the form's message catalog. Local $ref (#/...), allOf, and nullable types (type: ['string', 'null'], or anyOf with a null branch) are followed.
Not turned into fields, and reported in warnings instead: objects without properties, tuples, arrays of free-form values, remote $ref, and exclusiveMinimum / exclusiveMaximum. Conditional keywords (if / then, dependentRequired) are ignored; express those with appearCondition through overrides.
overrides is keyed by the same path the warnings use and is merged over the generated field. Use it to choose an application-specific type (textarea, a custom picker), or to attach hooks a schema cannot express.
A JSON Schema from fields
fieldsToJsonSchema goes the other way: it describes the data a field list collects, for an API contract, a validator on the server, or documentation.
import { fieldsToJsonSchema } from '@dynamic-field-kit/core';
const { schema, warnings } = fieldsToJsonSchema(fields, {
title: 'Sign-up',
defaults: { plan: 'free' },
types: { rating: { type: 'integer', minimum: 1, maximum: 5 } },
overrides: { username: { minLength: 3, pattern: '^[a-z]+$' } },
});| Field | Schema |
|---|---|
text, textarea, search, tel, color, hidden | string, with maxLength from props.maxLength |
email, url, password, date, time, datetime-local | string with the matching format (uri, date-time) |
number, range | number with minimum / maximum / multipleOf; integer when the step and minimum are whole |
checkbox, switch | boolean |
select, radio, or any field with static options | enum, or oneOf of const + title when a label differs from its value |
the same with multiple | array of that, with uniqueItems |
a repeatable group (fields) | array of objects, with minItems / maxItems and the defaultItem values as defaults |
a section (section) | nested object, with its own properties and required; its defaults go to its properties |
label becomes title, a string description the description, required the required list, and a field with computeValue is readOnly. defaults takes form data and records it as default; strict: true adds additionalProperties: false.
A schema cannot hold a function, so validate, appearCondition, readOnlyCondition and option loaders are listed in warnings rather than guessed at; put the rules they enforce in overrides, keyed by the same paths (username, contacts[].phone). A field type the kit does not know accepts any value until types describes it, and a file field is left out.