A Vue 3 JSON Schema based form editor component. Edit JSON in UI form with JSON Schema and element-plus, with full TypeScript support.
本仓库通过不同分支维护多个 Vue 版本的发布。请根据你的 Vue 版本选择对应分支:
| Branch | Version | Vue | UI Library | Status |
|---|---|---|---|---|
main |
3.x | Vue 3 (^3.5.0) |
element-plus (^2.11.1) |
✅ Active |
master |
2.x | Vue 2 (^2.7.16) |
element-ui (^2.15.14) |
🛠️ Maintenance |
v1 |
1.x | Vue 2 (^2.5.17) |
element-ui (^2.4.11) |
⚰️ Legacy |
npm install vue-json-ui-editor
# or
pnpm add vue-json-ui-editor
vue-json-ui-editor3.x targets Vue 3 and is designed to work with element-plus. For Vue 2, see themaster(2.x) orv1(1.x) branch.
<template>
<json-editor ref="jsonEditorRef" :schema="schema" v-model="model">
<el-button type="primary" @click="submit">Submit</el-button>
<el-button @click="reset">Reset</el-button>
</json-editor>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import JsonEditor from 'vue-json-ui-editor';
const schema = {
type: 'object',
title: 'vue-json-editor demo',
properties: {
name: { type: 'string' },
email: { type: 'string' },
},
};
const model = ref({ name: 'Yourtion' });
const jsonEditorRef = ref<InstanceType<typeof JsonEditor>>();
function submit() {
// jsonEditorRef.value?.form() returns the underlying element-plus form instance
jsonEditorRef.value?.form().validate((valid: boolean) => {
if (!valid) {
jsonEditorRef.value?.setErrorMessage('Please fill out the required fields');
}
});
}
function reset() {
jsonEditorRef.value?.reset();
}
</script>
json-editorrenders with native HTML elements by default. To style it with element-plus, register the components via the staticJsonEditor.setComponent(type, component, option?)API — see the example.Complete working example: example/components/Subscription.vue Schema: example/schema/newsletter.json
-
schemaObject (required) The JSON Schema object. Use thev-ifdirective to load asynchronous schema. -
v-model/modelValueObject (optional)default: {}Two-way binding for the form data. In Vue 3,v-modelbinds to themodelValueprop. -
auto-completeString (optional) Whether the value of the control can be automatically completed by the browser. Possible values:off,on. -
no-validateBoolean (optional)default: falseIndicates that the form is not to be validated when submitted. -
input-wrapping-classString (optional) Wraps each field's controls in a<div class="...">. Leaveundefinedto disable input wrapping. -
componentsObject (optional)default: undefinedPer-instance component overrides. When provided, these are merged over the global defaults registered viaJsonEditor.setComponent, so multiple<json-editor>instances on the same page can each use a different UI library without polluting each other. Keys are element types (e.g.text,select,form,label); values may be a full{ component, option }config or a shorthand component name/object. Leaveundefinedto fall back to the global registry.
// Two editors on the same page, each with its own text widget:
<json-editor :schema="a" :components="{ text: CompA }" />
<json-editor :schema="b" :components="{ text: CompB }" />-
update:modelValueEmitted (forv-model) whenever a field value changes. -
changeFired when a change to an element's value is committed by the user. -
submitFired when the form is submitted and passes validation. -
invalidFired when a submittable element has been checked and doesn't satisfy its constraints.
Exposed via a template ref to the <json-editor> instance (e.g. jsonEditorRef.value):
-
input(name)Get a form input reference. -
form()Get the rendered form component instance (e.g. the element-plusel-form), so you can call.validate()/.resetFields()on it. -
checkValidity()Checks whether the form satisfies its constraints. Returnsboolean. -
validate()Async validation shortcut — delegates to the underlying form component'svalidate()when available. -
reset()Reset the value of all elements of the form to the initialmodelValue. -
setErrorMessage(message)Set an error message (rendered via theerrorcomponent type). -
clearErrorMessage()Clear the error message. -
getFields()Return the current parsed field tree (including$subcontainers for nested objects). -
vmThe reactive view-model ({ model, fields, error }), for advanced consumers and option-callback access.
JsonEditor.setComponent(type, component, option?)Register a Vue component (or native tag name) for a given field/elementtype(e.g.form,label,email,text,select,error, …).optionmay be a plain object or a factory callback({ vm, field, item }) => propsObject. This is how you wire the editor to a UI library like element-plus.
JsonEditor.setComponent('text', 'el-input');
JsonEditor.setComponent('form', 'el-form', ({ vm }) => ({ model: vm.model, rules: {} }));
JsonEditor.setComponent('error', 'el-alert', ({ vm }) => ({ type: 'error', title: vm.error }));Note on
labelwith element-plus:el-form-itemreads its label text from thelabelprop (not the default slot), so thelabelregistration callback must returnlabel: field.labelin addition toprop: field.name. See example/components/Subscription.vue.
The widget for a field is chosen by attrs.type in its schema property (the editor reads schema.attrs as the field descriptor). Register the type once with setComponent, then drive it from the schema:
JsonEditor.setComponent('switch', 'el-switch');
JsonEditor.setComponent('date', 'el-date-picker');
// schema:
{ active: { type: 'boolean', attrs: { type: 'switch' } } }
{ createdAt: { type: 'string', format: 'date-time', attrs: { type: 'date' } } }disabled: trueandreadOnly: trueon a property disable/readonly the rendered input (readOnly also implies disabled).- Nested objects (
type: 'object'withproperties) render in a sub-container with the object'stitle(.sub-title) anddescription(.sub-description). - Object arrays (
type: 'array'withitems: { type: 'object', properties }) render as an editable list of sub-form rows. Each array renders a header (field label + add button,.json-editor-array-header) and one row per item (.json-editor-array-rowwith the item's sub-fields + a remove button). The add/remove buttons are registered via thearrayadd/arrayremovecomponent types (default native<button>; register asel-button/ icon buttons for UI library styling — see example/components/Subscription.vue). Arrays work at any nesting depth, including inside nested objects. - Choice arrays (
array+enum/oneOf/anyOf) render as a singleselect/ radio group / checkbox group.
The object-array add/remove logic is extracted into a standalone, framework-agnostic module so it can be reused or unit-tested in isolation:
import { createArrayRenderer, type ArrayRendererDeps } from 'vue-json-ui-editor';
const renderer = createArrayRenderer({
model, onChange, getComp, resolveComp, elementOptions, wrapChild, renderInput,
} satisfies ArrayRendererDeps);
renderer.render(field, fieldName); // → vnode[] for the whole array (header + rows)
renderer.addRow(fieldName);
renderer.removeRow(fieldName, index);ArrayRendererDeps declares exactly what the renderer needs (a reactive model, an onChange notifier, and the component-resolution helpers), so it isn't coupled to JsonEditor's setup closure.
This package ships with bundled type declarations. You can import types directly:
import JsonEditor, { type JsonSchema } from 'vue-json-ui-editor';
// newly exported types (v3.1+):
import type {
JsonEditorStatic, // the setComponent static method signature
JsonEditorInstance, // exposed instance methods (form/validate/reset/getFields/vm)
ComponentConfig, OptionContext, VmContext, ComponentsMap,
ArrayRendererDeps, // for createArrayRenderer
} from 'vue-json-ui-editor';pnpm install # install dependencies
pnpm dev # start the example app (Vite)
pnpm build:lib # build the publishable library bundle
pnpm test # run unit tests (Vitest)
pnpm check # type-check + format + testMIT © Yourtion
