Project Name | Stars | Downloads | Repos Using This | Packages Using This | Most Recent Commit | Total Releases | Latest Release | Open Issues | License | Language |
---|---|---|---|---|---|---|---|---|---|---|
React Jsonschema Form | 12,444 | 566 | 281 | 13 hours ago | 103 | December 16, 2019 | 296 | apache-2.0 | TypeScript | |
A React component for building Web forms from JSON Schema. | ||||||||||
Ajv | 12,308 | 801,606 | 7,985 | 2 days ago | 349 | March 22, 2022 | 252 | mit | TypeScript | |
The fastest JSON schema Validator. Supports JSON Schema draft-04/06/07/2019-09/2020-12 and JSON Type Definition (RFC8927) | ||||||||||
Quicktype | 9,677 | 42 | 90 | 2 days ago | 674 | September 12, 2022 | 395 | apache-2.0 | TypeScript | |
Generate types and converters from JSON, Schema, and GraphQL | ||||||||||
Jsonschema2pojo | 5,945 | 115 | 34 | 7 days ago | 52 | May 03, 2022 | 161 | apache-2.0 | Java | |
Generate Java types from JSON or JSON Schema and annotate those types for data-binding with Jackson, Gson, etc | ||||||||||
Json Editor | 5,376 | 141 | 37 | 5 years ago | 19 | August 07, 2016 | 461 | mit | JavaScript | |
JSON Schema Based Editor | ||||||||||
Resume Cli | 4,293 | 93 | 34 | 12 days ago | 106 | April 20, 2022 | 98 | mit | JavaScript | |
CLI tool to easily setup a new resume 📑 | ||||||||||
Jsonschema | 4,073 | 15,530 | 2,424 | a day ago | 74 | July 07, 2022 | 30 | mit | Python | |
An implementation of the JSON Schema specification for Python | ||||||||||
Mimesis | 3,936 | 42 | 19 | 2 days ago | 37 | June 22, 2022 | 18 | mit | Python | |
Mimesis is a robust fake data generator for Python, which provides data for a variety of purposes in a variety of languages. | ||||||||||
Json Schema | 3,344 | 8,501 | 489 | 3 months ago | 46 | April 13, 2022 | 96 | mit | PHP | |
PHP implementation of JSON schema. Fork of the http://jsonschemaphpv.sourceforge.net/ project | ||||||||||
Fast Json Stringify | 3,142 | 747 | 135 | 5 days ago | 136 | September 26, 2022 | 14 | mit | JavaScript | |
2x faster than JSON.stringify() |
Vue component form based on JSON Schema and Native HTML
npm install --save @formschema/native
<template>
<FormSchema :schema="schema" v-model="model" @submit.prevent="submit">
<button type="submit">Subscribe</button>
</FormSchema>
</template>
<script>
import FormSchema from '@formschema/native'
import schema from './schema/newsletter-subscription.json'
export default {
data: () => ({
schema: schema,
model: {}
}),
methods: {
submit (e) {
// this.model contains the valid data according your JSON Schema.
// You can submit your model to the server here
}
},
components: { FormSchema }
}
</script>
text
to render schema with type: 'string'
number
to render schema with type: 'number' | 'integer'
hidden
to render schema with type: 'null'
checkbox
to render schema with type: 'boolean'
maximum
, minimum
, exclusiveMaximum
and exclusiveMinimum
to generate HTML attributes max
and min
.maxLength
, minLength
, pattern
to generate HTML attributes maxlength
, minlength
and pattern
.date
to render schema with format: 'date'
datetime-local
to render schema with format: 'date-time'
email
to render schema with format: 'email' | 'idn-email'
time
to render schema with format: 'time'
url
to render schema with format: 'uri'
step
contentMediaType
is equal to text/*
, the HTML element <textarea/>
is used for renderingcontentMediaType
is not equal to text/*
, the HTML element <input/>
with attributes { type: file, accept: contentMediaType }
is used for renderingSince FormSchema is just a form generator, some JSON Schema validation keywords are irrelevant:
Name | Type | Description | Default |
---|---|---|---|
schema required
|
Object |
The input JSON Schema object. | |
v-model |
any |
Use this directive to create two-way data bindings with the component. It automatically picks the correct way to update the element based on the input type. | undefined |
id |
String |
The id property of the Element interface represents the form's identifier, reflecting the id global attribute. | Random unique ID |
name |
String |
The name of the form. It must be unique among the forms in a document. | undefined |
bracketed-object-input-name |
Boolean |
When set to true (default), checkbox inputs and nested object inputs will * automatically include brackets at the end of their names (e.g. name="grouped-checkbox-fields[]" ). Setting this property to false , disables this behaviour. |
true |
search |
Boolean |
Use this prop to enable search landmark role to identify a section of the page used to search the page, site, or collection of sites. |
false |
disabled |
Boolean |
Indicates whether the form elements are disabled or not. | false |
components |
ComponentsLib |
Use this prop to overwrite the default Native HTML Elements with custom components. | GLOBAL.Elements |
descriptor |
DescriptorInstance |
UI Schema Descriptor to use for rendering. | {} |
validator |
Function |
The validator function to use to validate data before to emit the input event.Syntax function validator(field: GenericField): Promise<boolean> Parameters
A promise that return true if validation success and false otherwise |
null |
Name | Description |
---|---|
input |
Fired synchronously when the value of an element is changed. Arguments
|
Get the HTML form object reference.
Example
<template>
<FormSchema ref="formSchema" :schema="schema"/>
</template>
<script>
import FormSchema from '@formschema/native'
export default {
components: { FormSchema },
data: () => ({
schema: { type: 'string' }
}),
mounted() {
console.log(this.$refs.formSchema.form())
}
};
</script>
Syntax
form(): HTMLFormElement | VNode | undefined
Return value
An HTMLFormElement
object or a VNode
object describing the form element
object, or undefined
for input JSON schema object.
<template>
<FormSchema :schema="schema"/>
</template>
<script>
import axios from 'axios'
import FormSchema from '@formschema/native'
export default {
components: { FormSchema },
data: () => ({
schema: {}
}),
created() {
axios.get('/api/schema/subscription.json').then(({ data: schema }) => {
this.schema = schema
});
}
};
</script>
Load an async schema on the beforeRouterEnter
hook:
<template>
<FormSchema :schema="schema"/>
</template>
<script>
import axios from 'axios'
import FormSchema from '@formschema/native'
export default {
components: { FormSchema },
data: () => ({
schema: {}
}),
beforeRouterEnter(from, to, next) {
axios.get('/api/schema/subscription.json')
.then(({ data: schema }) => next((vm) => vm.setSchema(schema)))
.catch(next);
},
methods: {
setSchema(schema) {
this.schema = schema;
}
}
};
</script>
To load a JSON Schema with $ref
pointers, you need to install an additional dependency to resolve them:
import $RefParser from 'json-schema-ref-parser';
import FormSchema from '@formschema/native';
import schemaWithPointers from './schema/with-pointers.json';
export default {
components: { FormSchema },
data: () => ({
schema: {}
}),
created () {
$RefParser.dereference(schemaWithPointers)
.then((schema) => {
// `schema` is the resolved schema that contains your entire JSON
// Schema, including referenced files, combined into a single object
this.schema = schema;
});
}
}
See json-schema-ref-parser documentation page for more details.
By default, FormSchema uses basic HTML5 validation by applying validation attributes on inputs. This is enough for simple schema, but you will need to dedicated JSON Schema validator if you want to validate complex schema.
For custom validation, you need to provide a validation function prop.
Bellow the custom validation API:
type MessageInfo = 0;
type MessageSuccess = 1;
type MessageWarining = 2;
type MessageError = 3;
type MessageType = MessageInfo | MessageSuccess | MessageWarining | MessageError;
interface Message {
type?: MessageType;
text: string;
}
interface GenericField<TModel = any> {
readonly id: string;
readonly key: string;
readonly name: string;
readonly isRoot: boolean;
readonly schema: JsonSchema;
readonly required: boolean;
readonly hasChildren: boolean;
readonly initialValue: TModel;
readonly value: TModel;
readonly messages: Required<Message>[];
clear(): void; // clear field
reset(): void; // reset initial field value
addMessage(message: string, type: MessageType = MessageError): void;
clearMessages(recursive: boolean = false): void;
}
Bellow a basic example with the popular AJV validator:
<template>
<FormSchema v-model="model" v-bind="{ schema, validator }" @submit.prevent="onSubmit">
<button type="submit">Submit</button>
</FormSchema>
</template>
<script>
import Ajv from 'ajv';
import FormSchema from '@formschema/native';
export default {
data: () => ({
schema: {
type: 'object',
properties: {
username: {
type: 'string',
minLength: 5
},
password: {
type: 'string',
minLength: 6
}
},
required: ['username', 'password']
},
model: {},
ajv: new Ajv({ allErrors: true })
}),
computed: {
validate() {
return this.ajv.compile(this.schema);
}
},
methods: {
onSubmit({ field }) {
if (field && this.validator(field)) {
// validation success, submit code here
}
},
validator(field) {
// Clear all messages
field.clearMessages(true);
if (!this.validate(field.value)) {
this.validate.errors.forEach(({ dataPath, message }) => {
const errorField = field.hasChildren
? field.getField(dataPath) || field
: field;
/**
* Add a message to `errorField`.
* The first argument is the message string
* and the second one the message type:
* 0 - Message Info
* 1 - Message Success
* 2 - Message Warning
* 3 - Message Error
*/
errorField.addMessage(message, 3);
});
// Return `false` to cancel the `input` event
return Promise.resolve(false);
}
// Return `true` to trigger the `input` event
return Promise.resolve(true);
}
},
components: { FormSchema }
};
</script>
Since FormSchema use the native HTML Form element, attributes novalidate
and
formvalidate
can be used to disable form validation as it described in the
W3C specification:
If present, they indicate that the form is not to be validated during submission.
The no-validate state of an element is true if the element is a submit button and the element'sformnovalidate
attribute is present, or if the element's form owner'snovalidate
attribute is present, andfalse
otherwise.
novalidate
<template>
<FormSchema v-model="model" :schema="schema" novalidate>
<button type="submit">Submit</button>
</FormSchema>
</template>
Disable the form validation constraints could be useful when implementing a save feature to the form:
<template>
<FormSchema v-model="model" :schema="schema" action="/api/blog/post" method="post">
<input type="submit" name="submit" value="Submit">
<input type="submit" formnovalidate name="save" value="Save">
<input type="submit" formnovalidate name="cancel" value="Cancel">
</FormSchema>
</template>
The simple way to translate labels without to change the JSON Schema file is to use a descriptor.
Here an example with Vue I18n:
<template>
<FormSchema v-model="model" :schema="schema" :descriptor="descriptor"/>
</template>
<script>
import FormSchema from '@formschema/native';
export default {
data: () => ({
schema: {
type: 'object',
properties: {
firstname: {
type: 'string'
},
lastname: {
type: 'string'
}
}
},
model: {}
}),
computed: {
descriptor() {
properties: {
firstname: {
label: this.$t('firstname.label'),
helper: this.$t('firstname.helper')
},
lastname: {
label: this.$t('lastname.label'),
helper: this.$t('lastname.helper')
}
}
}
},
// `i18n` option, setup locale info for component
// see https://kazupon.github.io/vue-i18n/guide/component.html
i18n: {
messages: {
en: {
firstname: {
label: 'First Name',
helper: 'Your First Name'
},
lastname: {
label: 'Last Name',
helper: 'Your Last Name'
}
},
fr: {
firstname: {
label: 'Prénom',
helper: 'Votre prénom'
},
lastname: {
label: 'Nom',
helper: 'Votre nom'
}
}
}
},
components: { FormSchema }
};
</script>
Add a text/*
media types to a string schema to render a Textarea element.
Example schema.json
{
"type": "string",
"contentMediaType": "text/plain"
}
You can also use a descriptor to force the Render to use a Textarea element:
Example descriptor.json
{
"kind": "textarea"
}
String schemas with media types not starting with text/*
are automatically render as Input File elements.
Example schema.json
{
"type": "string",
"contentMediaType": "image/png"
}
There is a list of MIME types officially registered by the IANA, but the set of types supported will be application and operating system dependent. Mozilla Developer Network also maintains a shorter list of MIME types that are important for the web.
Schemas with descriptor's kind hidden
are render as hidden input elements.
Example schema.json
{
"type": "string"
}
Example descriptor.json
{
"kind": "hidden"
}
String schemas with a descriptor's kind password
are used to render Input
Password elements.
Example schema.json
{
"type": "string"
}
Example descriptor.json
{
"kind": "password"
}
To define multiple checkbox, use the JSON Schema keyword enum
and uniqueItems:
Example schema.json
{
"type": "array",
"uniqueItems": true,
"enum": {
"type": "string",
"enum": [
"daily",
"promotion"
]
}
}
Example descriptor.json
{
"items": {
"daily": {
"label": "Receive daily updates"
},
"promotion": {
"label": "Receive promotion emails"
}
}
}
To group radio elements, use the JSON Schema keyword enum
with a enum
descriptor:
Example schema.json
{
"type": "string",
"enum": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday"
]
}
Example descriptor.json
{
"kind": "enum",
"items": {
"monday": { "label": "Monday" },
"tuesday": { "label": "Tuesday" },
"wednesday": { "label": "Wednesday" },
"thursday": { "label": "Thursday" },
"friday": { "label": "Friday" },
"saturday": { "label": "Saturday" },
"sunday": { "label": "Sunday" }
}
}
To group HTML Select element, use the JSON Schema keyword enum
with a list
descriptor:
Example schema.json
{
"type": "string",
"enum": [
"monday",
"tuesday",
"wednesday",
"thruday",
"friday",
"saturday",
"sunday"
]
}
Example descriptor.json
{
"kind": "list",
"items": {
"monday": { "label": "Monday" },
"tuesday": { "label": "Tuesday" },
"wednesday": { "label": "Wednesday" },
"thursday": { "label": "Thursday" },
"friday": { "label": "Friday" },
"saturday": { "label": "Saturday" },
"sunday": { "label": "Sunday" }
}
}
To render a array field, define your schema like:
Example schema.json
{
"type": "array",
"items": {
"type": "string"
}
}
FormSchema
will render a text input by adding a button to add more inputs.
To render a regex input, define your schema like:
Example schema.json
{
"type": "string",
"pattern": "[a-e]+"
}
FormSchema use a <fieldset>
element to group inputs of a object JSON Schema:
Example schema.json
{
"type": "object",
"properties": {
"firstname": {
"type": "string"
},
"lastname": {
"type": "string"
}
},
"required": ["firstname"]
}
Use descriptor to set labels and helpers. You can also change the order of properties for the rendering:
Example descriptor.json
{
"properties": {
"firstname": {
"label": "First Name",
"helper": "Your first name"
},
"lastname": {
"label": "Last Name",
"helper": "Your last name"
}
},
"order": ["lastname", "firstname"]
}
type SchemaType = 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null';
type ScalarKind = 'string' | 'password' | 'number' | 'integer' | 'null' | 'boolean' | 'hidden' | 'textarea' | 'image' | 'file' | 'radio' | 'checkbox';
type ItemKind = 'enum' | 'list';
type FieldKind = SchemaType | ScalarKind | ItemKind;
type ComponentsType = 'form' | 'message' | 'button' | 'helper' | FieldKind;
type Component = string | VueComponent | VueAsyncComponent;
interface IComponents {
set(kind: ComponentsType, component: Component): void;
get(kind: ComponentsType, fallbackComponent?: Component): Component;
}
To define custom elements, you need to use the NativeComponents
class and the
components
prop:
// MyCustomComponents.js
// First, import the base class Components from `@formschema/native` package
import { NativeComponents } from '@formschema/native';
// Then declare your custom components as functional components
import { InputElement } from '@/components/InputElement';
import { StateElement } from '@/components/StateElement';
import { ArrayElement } from '@/components/ArrayElement';
import { FieldsetElement } from '@/components/FieldsetElement';
import { ListElement } from '@/components/ListElement';
import { TextareaElement } from '@/components/TextareaElement';
import { MessageElement } from '@/components/Message';
// Finaly, extend the NativeComponents class and define override it
export class MyCustomComponents extends NativeComponents {
constructor() {
super();
this.set('array', ArrayElement);
this.set('boolean', StateElement);
this.set('string', InputElement);
this.set('password', InputElement);
this.set('file', InputElement);
this.set('image', InputElement);
this.set('radio', StateElement);
this.set('checkbox', StateElement);
this.set('enum', FieldsetElement);
this.set('number', InputElement);
this.set('integer', InputElement);
this.set('object', FieldsetElement);
this.set('list', ListElement);
this.set('textarea', TextareaElement);
this.set('message', MessageElement);
}
}
See the file NativeComponents.ts for an example.
<template>
<FormSchema v-model="model" :schema="schema" :components="components"/>
</template>
<script>
import FormSchema from '@formschema/native'
import { MyCustomComponents } from './MyCustomComponents'
export default {
data: () => ({
schema: { /* ... */ },
components: new MyCustomComponents(),
model: {}
}),
components: { FormSchema }
}
</script>
type SchemaType = 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null';
type ParserKind = SchemaType | 'enum' | 'list' | 'textarea' | 'image' | 'file' | 'password';
type ScalarKind = 'string' | 'password' | 'number' | 'integer' | 'null' | 'boolean' | 'hidden' | 'textarea' | 'image' | 'file' | 'radio' | 'checkbox';
type ItemKind = 'enum' | 'list';
type FieldKind = SchemaType | ScalarKind | ItemKind;
type ComponentsType = 'form' | 'message' | 'button' | 'helper' | FieldKind;
type Component = string | VueComponent | AsyncVueComponent;
type SetDescriptor = EnumDescriptor | ArrayDescriptor | ObjectDescriptor;
type Descriptor = ScalarDescriptor | SetDescriptor | ListDescriptor;
type DescriptorConstructor = (field: Field) => Descriptor;
type DescriptorInstance = Descriptor | DescriptorConstructor;
interface DescriptorDefinition<TKind extends FieldKind = FieldKind> {
kind?: TKind;
label?: string;
helper?: string;
visible?: boolean; // by default true. If false, component will be ignored on rendering
component?: Component;
attrs?: {
[attr: string]: unknown;
};
props?: {
[prop: string]: unknown;
};
}
/**
* Describe scalar types like: string, password, number, integer,
* boolean, null, hidden field, textarea element, image and file
* inputs, radio and checkbox elements
*/
interface ScalarDescriptor extends DescriptorDefinition<ScalarKind> {
}
/**
* Use to describe grouped object properties
*/
interface ObjectGroupDescriptor extends DescriptorDefinition {
properties: string[];
}
/**
* Describe JSON Schema with type `object`
*/
interface ObjectDescriptor extends DescriptorDefinition {
layout?: Component; // default: 'fieldset'
properties?: {
[schemaProperty: string]: DescriptorInstance;
};
order?: string[];
groups?: {
[groupId: string]: ObjectGroupDescriptor;
};
}
/**
* Describe JSON Schema with key `enum`
*/
interface ItemsDescriptor<TKind extends ItemKind> extends DescriptorDefinition<TKind> {
items?: {
[itemValue: string]: ScalarDescriptor;
};
}
/**
* Describe HTML Radio Elements
*/
interface EnumDescriptor extends ItemsDescriptor<'enum'> {
layout?: Component; // default: 'fieldset'
}
/**
* Describe HTML Select Element
*/
interface ListDescriptor extends ItemsDescriptor<'list'> {
}
/**
* Describe buttons for array schema
*/
interface ButtonDescriptor<T extends string, A extends Function> extends Partial<ActionButton<A>> {
type: T;
label: string;
tooltip?: string;
visible?: boolean;
component?: Component;
}
type ActionPushTrigger = () => void;
type PushButtonDescriptor = ButtonDescriptor<'push', ActionPushTrigger>;
type MoveUpButtonDescriptor = ButtonDescriptor<'moveUp', ActionPushTrigger>;
type MoveDownButtonDescriptor = ButtonDescriptor<'moveDown', ActionPushTrigger>;
type DeleteButtonDescriptor = ButtonDescriptor<'delete', ActionPushTrigger>;
type UnknownButtonDescriptor = ButtonDescriptor<string, ActionPushTrigger>;
type ArrayItemButton = MoveUpButtonDescriptor
| MoveDownButtonDescriptor
| DeleteButtonDescriptor
| UnknownButtonDescriptor;
/**
* Describe JSON Schema with type `array`
*/
interface ArrayDescriptor extends DescriptorDefinition {
layout?: Component; // default: 'fieldset'
items?: DescriptorInstance[] | DescriptorInstance;
pushButton: PushButtonDescriptor | null;
buttons: ArrayItemButton[];
}
FormSchema can help you organize your form fields by grouping them. Please check ObjectDescriptor definition.
Example schema.json
{
"type": "object",
"properties": {
"lastname": {
"type": "string"
},
"firstname": {
"type": "string"
},
"city": {
"type": "string"
}
}
}
Example descriptor.json
{
"groups": {
"name": {
"label": "Your name",
"properties": ["firstname", "lastname"]
},
"location": {
"label": "Your location",
"properties": ["city"]
}
}
}
<form id="id-form">
<fieldset id="id-form-field">
<div data-fs-kind="string" data-fs-type="text" data-fs-field="lastname"><label for="id-form-field-lastname"></label>
<div data-fs-input="text"><input id="id-form-field-lastname" type="text" name="lastname"></div>
</div>
<div data-fs-kind="string" data-fs-type="text" data-fs-field="firstname"><label for="id-form-field-firstname"></label>
<div data-fs-input="text"><input id="id-form-field-firstname" type="text" name="firstname"></div>
</div>
<div data-fs-kind="string" data-fs-type="text" data-fs-field="city"><label for="id-form-field-city"></label>
<div data-fs-input="text"><input id="id-form-field-city" type="text" name="city"></div>
</div>
</fieldset>
</form>
<form id="id-form">
<fieldset id="id-form-field">
<div data-fs-group="name">
<div data-fs-group-label="name">Your name</div>
<div data-fs-group-nodes="2">
<div data-fs-kind="string" data-fs-type="text" data-fs-field="firstname"><label for="id-form-field-firstname"></label>
<div data-fs-input="text"><input id="id-form-field-firstname" type="text" name="firstname"></div>
</div>
<div data-fs-kind="string" data-fs-type="text" data-fs-field="lastname"><label for="id-form-field-lastname"></label>
<div data-fs-input="text"><input id="id-form-field-lastname" type="text" name="lastname"></div>
</div>
</div>
</div>
<div data-fs-group="location">
<div data-fs-group-label="location">Your location</div>
<div data-fs-group-nodes="1">
<div data-fs-kind="string" data-fs-type="text" data-fs-field="city"><label for="id-form-field-city"></label>
<div data-fs-input="text"><input id="id-form-field-city" type="text" name="city"></div>
</div>
</div>
</div>
</fieldset>
</form>
Please see contributing guide.
Under the MIT license. See LICENSE file for more details.