@uponu-solutions/jsonjoy-builder
v0.9.0
Published
Visual JSON Schema editor for creating and manipulating JSON Schema definitions with an intuitive interface.
Readme
JSON Schema Builder
A modern, React-based visual JSON Schema editor for creating and manipulating JSON Schema definitions with an intuitive interface.
Try online: https://json.ophir.dev
Features
- Visual Schema Editor: Design your JSON Schema through an intuitive interface without writing raw JSON
- Real-time JSON Preview: See your schema in JSON format as you build it visually
- Schema Inference: Generate schemas automatically from existing JSON data
- JSON Validation: Test JSON data against your schema with detailed validation feedback
- Responsive Design: Fully responsive interface that works on desktop and mobile devices
Getting Started
Installing
npm install jsonjoy-builderAlso install react if you haven't done so yet.
Then use like this:
import "jsonjoy-builder/styles.css";
import { type JSONSchema, SchemaVisualEditor } from "jsonjoy-builder";
import { useState } from "react";
export function App() {
const [schema, setSchema] = useState<JSONSchema>({});
return (
<div>
<h1>JSONJoy Builder</h1>
<SchemaVisualEditor schema={schema} onChange={setSchema}/>
</div>
);
}Styling
To style the component, add custom CSS. For basic styling, there are some CSS custom properties ("variables) you can set:
.jsonjoy {
--jsonjoy-background: #f8fafc;
--jsonjoy-foreground: #020817;
--jsonjoy-card: #fff;
--jsonjoy-card-foreground: #020817;
--jsonjoy-popover: #fff;
--jsonjoy-popover-foreground: #020817;
--jsonjoy-primary: #0080ff;
--jsonjoy-primary-foreground: #f8fafc;
--jsonjoy-secondary: #f1f5f9;
--jsonjoy-secondary-foreground: #0f172a;
--jsonjoy-muted: #f1f5f9;
--jsonjoy-muted-foreground: #64748b;
--jsonjoy-accent: #f1f5f9;
--jsonjoy-accent-foreground: #0f172a;
--jsonjoy-destructive: #ef4444;
--jsonjoy-destructive-foreground: #f8fafc;
--jsonjoy-border: #e2e8f0;
--jsonjoy-input: #e2e8f0;
--jsonjoy-ring: #020817;
--jsonjoy-radius: .8rem;
--jsonjoy-font-sans: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
.jsonjoy.dark {
/** same, but for dark mode */
}Localization
By default, the editor uses English. To localize, you need to set a language via the TranslationContext:
import "jsonjoy-builder/styles.css";
import { type JSONSchema, SchemaVisualEditor, TranslationContext, de } from "jsonjoy-builder";
import { useState } from "react";
export function App() {
const [schema, setSchema] = useState<JSONSchema>({});
return (
<TranslationContext value={de}>
<SchemaVisualEditor schema={schema} onChange={setSchema}/>
</TranslationContext>
);
}Currently we have localizations for English, German, French and Russian. You can define your own translation like this. If you do, consider opening a PR with the translations!
import { type Translation } from "jsonjoy-builder";
const es: Translation = {
// add translations here (see type Translation for the available keys and default values)
};See also the English localizations file for the default localizations.
Field extensions
SchemaVisualEditor takes an optional extensions prop to add settings of your own to every
field — for example custom keywords such as x-ui stored on the field's schema:
<SchemaVisualEditor
schema={schema}
onChange={setSchema}
extensions={{
// Below the type specific settings of an expanded field.
renderFieldSettings: ({ name, schema, parent, readOnly, onChange }) => (
<MySettings schema={schema} onChange={onChange} disabled={readOnly} />
),
// In the field's header, before the type dropdown.
renderFieldBadges: ({ schema }) => (schema["x-ui"] ? <Badge>ui</Badge> : null),
// Update references to a renamed property among its siblings.
onPropertyRenamed: (parent, oldName, newName) => parent,
// Leave out the allowed values input of text fields, e.g. to edit them in your own settings.
hideAllowedValues: true,
}}
/>parent is the object schema whose properties hold the field, so settings can refer to sibling
fields. Without extensions the editor renders exactly as before.
Custom field types
customTypes adds kinds of fields of your own to the type dropdown, listed after the JSON types.
A field whose schema matches one shows its label and color, no type settings and, unless the type
canBeRequired, no required dropdown (a static element cannot be required). The name, label and
description inputs, renderFieldBadges and renderFieldSettings stay available. Choosing a JSON
type for such a field keeps only its label and description.
inputs says what the label (title) and description inputs mean for the kind: a label, a help
text (description) and a placeholder of their own, multiline for a growing text area (e.g.
Markdown), or false to leave an input out when the kind does not use that text.
const heading: CustomFieldType = {
id: "heading",
label: "Heading",
description: "A static heading, not a value",
color: "teal",
// The field is a heading when it carries the custom keyword.
matches: (schema) => typeof schema === "object" && schema["x-heading"] === true,
// The schema the field gets when switched to a heading; keep a JSON `type`.
create: (schema) => ({
type: "string",
title: schema.title,
description: schema.description,
"x-heading": true,
}),
// The label is the heading's text; a heading has no description.
inputs: {
title: { label: "Heading text", description: "Shown as the heading in the form" },
description: false,
},
};
<SchemaVisualEditor
schema={schema}
onChange={setSchema}
extensions={{ customTypes: [heading] }}
/>;Sections
sections groups the root properties into boxes, e.g. the pages of a form. A property for which
isStart returns true is the header of a section; the properties after it, up to the next start,
are its fields and sit inside the box. The start keeps all of its row behaviour (expand to edit,
badges, type dropdown, delete). Properties before the first start form an implicit first section,
titled firstTitle, shown only when it has fields; it cannot be dragged or deleted.
- Sections apply to the root list only, never inside objects or lists.
- The "Add field" button below the list is replaced by one per section, which inserts at the end of that section.
- Drag a field to another section, or a whole section by the handle of its start property.
- Deleting a start property removes only that property; its fields merge into the previous section (into the implicit first section when it was the first start).
- With
addSection, a button below the boxes (outside of every section) appends a new start property: keyedaddSection.name(with a counter when taken), with the schema ofaddSection.create(), which has to matchisStart. - Without a section start (or without
sections) the editor renders as before.
The schema stays the flat property list, so sections are presentation only.
const pageBreak: CustomFieldType = {
id: "pageBreak",
label: "Page break",
matches: (schema) => typeof schema === "object" && schema["x-page"] === true,
create: (schema) => ({ type: "string", title: schema.title, "x-page": true }),
};
<SchemaVisualEditor
schema={schema}
onChange={setSchema}
extensions={{
customTypes: [pageBreak],
sections: {
isStart: (schema) => pageBreak.matches(schema),
firstTitle: "Page 1",
addSection: {
label: "Add page",
name: "page",
create: () => ({ type: "string", "x-page": true }),
},
},
}}
/>;Development
git clone https://github.com/lovasoa/jsonjoy-builder.git
cd jsonjoy-builder
npm installStart the development server:
npm run devThe demo application will be available at http://localhost:5173
Building for Production
Build this library for production:
npm run buildThe built files will be available in the dist directory.
Project Architecture
Core Components
- JsonSchemaEditor: The main component that provides tabs for switching between visual and JSON views
- SchemaVisualEditor: Handles the visual representation and editing of schemas
- JsonSchemaVisualizer: Provides JSON view with Monaco editor for direct schema editing
- SchemaInferencer: Dialog component for generating schemas from JSON data
- JsonValidator: Dialog component for validating JSON against the current schema
Key Features
Schema Inference
The SchemaInferencer component can automatically generate JSON Schema definitions from existing JSON data. This feature uses a recursion-based inference system to detect:
- Object structures and properties
- Array types and their item schemas
- String formats (dates, emails, URIs)
- Numeric types (integers vs. floats)
- Required fields
JSON Validation
Validate any JSON document against your schema with:
- Real-time feedback
- Detailed error reporting
- Format validation for emails, dates, and other special formats
Technology Stack
- React: UI framework
- TypeScript: Type-safe development
- Rsbuild / Rslib: Build tool and development server
- ShadCN UI: Component library
- Monaco Editor: Code editor for JSON viewing/editing
- Ajv: JSON Schema validation
- Zod: Type-safe json parsing in ts
- Lucide Icons: Icon library
- Node.js Test Runner: Simple built-in testing
Development Scripts
| Command | Description |
|---------|-------------|
| npm run dev | Start development server |
| npm run build | Build for production |
| npm run build:dev | Build with development settings |
| npm run lint | Run linter |
| npm run format | Format code |
| npm run check | Type check the project |
| npm run fix | Fix linting issues |
| npm run typecheck | Type check with TypeScript |
| npm run preview | Preview production build |
| npm run test | Run tests |
License
This project is licensed under the MIT License - see the LICENSE file for details.
