1.4.2 • Published 7 months ago

@satoshibits/react-json-schema-builder v1.4.2

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
7 months ago

React JSON Schema Builder

A powerful React component for visually building and editing JSON Schema definitions.

Demo: https://abetoots.github.io/react-json-schema-builder-demo/

Features

  • 🧰 Visual interface for building JSON Schema documents
  • 🔄 Real-time schema updates
  • 📊 Support for nested objects and arrays
  • ⚙️ Property constraints for different data types
  • 🧩 Extensible plugin system for custom constraints
  • 🎨 Customizable property rendering
  • 🧠 Built-in schema validation
  • 🌲 Navigation through nested object structures
  • ✨ Customizable UI elements (navigation, property list, add button)
  • 🔒 Support for read-only properties and disabled mode

Installation

npm install @satoshibits/react-json-schema-builder
# or
yarn add @satoshibits/react-json-schema-builder
# or
pnpm add @satoshibits/react-json-schema-builder

Basic Usage

Here's a simple example of how to use the SchemaBuilder component:

import {
  SchemaBuilder,
  SchemaProvider,
  PluginsProvider,
} from "@satoshibits/react-json-schema-builder";
import { useState } from "react";
import type { JSONSchema7 } from "json-schema";

// Import the styles
import "@satoshibits/react-json-schema-builder/index.css";

// Import plugins for constraint support
import { NumberConstraintPlugin } from "@satoshibits/react-json-schema-builder/plugins/number";
import { StringConstraintPlugin } from "@satoshibits/react-json-schema-builder/plugins/string";
import { ArrayConstraintPlugin } from "@satoshibits/react-json-schema-builder/plugins/array";
import { ObjectConstraintPlugin } from "@satoshibits/react-json-schema-builder/plugins/object";
import { EnumConstraintPlugin } from "@satoshibits/react-json-schema-builder/plugins/enum";

function SchemaEditorApp() {
  const [currentSchema, setCurrentSchema] = useState<JSONSchema7>();

  const initialSchema = useMemo(() => {
    return {
      type: "object",
      properties: {
        name: {
          type: "string",
          title: "Name",
          description: "The user's full name",
        },
        age: {
          type: "integer",
          title: "Age",
          description: "The user's age in years",
        },
      },
      required: ["name"],
    } satisfies JSONSchema7;
  }, []);

  const handleSchemaChange = (newSchema: JSONSchema7) => {
    setCurrentSchema(newSchema);
    console.log("Schema updated:", newSchema);
  };

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-2xl font-bold mb-4">JSON Schema Builder</h1>

      <div className="border rounded p-4">
        <SchemaProvider>
          <PluginsProvider>
            <SchemaBuilder
              initialSchema={initialSchema}
              onSchemaChange={handleSchemaChange}
              plugins={[
                NumberConstraintPlugin,
                StringConstraintPlugin,
                ArrayConstraintPlugin,
                ObjectConstraintPlugin,
                EnumConstraintPlugin,
              ]}
            />
          </PluginsProvider>
        </SchemaProvider>
      </div>

      <div className="mt-4">
        <h2 className="text-lg font-semibold">Generated Schema:</h2>
        <pre className="bg-gray-100 p-4 rounded">
          {JSON.stringify(schema, null, 2)}
        </pre>
      </div>
    </div>
  );
}

export default SchemaEditorApp;

API Reference

SchemaBuilder Component Props

PropTypeDescription
schemaJSONSchema7The JSON Schema to edit. Should be an object schema.
pluginsBaseJSONSchemaPlugin[]Array of constraint plugins to support different property types.
propertyComponentReact.ComponentType<PropertyComponentProps>Optional custom component to render properties.
onSchemaChange(schema: JSONSchema7) => voidCalled whenever the schema changes.
onPropertyAddSuccess(props: PropertyAddSuccess) => voidCalled when a property is successfully added.
onPropertyAddError(props: PropertyAddError) => voidCalled when there's an error adding a property.
onPropertyChangeError(prop: Error) => voidCalled when there's an error changing a property.
onPropertyChangeSuccess() => voidCalled when a property is successfully changed.
backButtonClassNamestringClass name for the navigation bar's back button.
propertiesListClassNamestringClass name for the properties list container.
addPropertyButtonClassNamestringClass name for the Add Property button.
navigationComponentReact.ComponentType<NavigationProps>Custom component for the navigation bar.
addPropertyButtonComponentReact.ComponentType<AddPropertyButtonProps>Custom component for the Add Property button.
disabledPropertiesstring[]Array of property keys that should be disabled (read-only).
disabledbooleanWhether all properties should be disabled (read-only).

Navigation and Add Button Props

When providing custom navigation or add button components, they will receive these props:

// For custom navigation
interface NavigationProps {
  currentPath: string[];
  onNavigateUp: () => void;
  onNavigateTo: (newPath: string[]) => void;
  isRootPath: boolean;
}

// For custom add property button
interface AddPropertyButtonProps {
  onClick: () => void;
}

PropertyComponentProps Interface

When providing a custom property component, it will receive these props:

interface PropertyComponentProps {
  property: {
    key: string;
    schema: JSONSchema7;
    isRequired: boolean;
  };
  onPropertyChange: (key: string, changes: Partial<JSONSchema7>) => void;
  onKeyChange: (oldKey: string, newKey: string) => void;
  onDelete: (key: string) => void;
  onDuplicate?: (key: string) => void;
  onNavigate?: (key: string) => void;
  disabled?: boolean;
}

Plugins

The SchemaBuilder supports different types of constraints for properties through plugins:

  • NumberConstraintPlugin: Adds constraints like minimum, maximum, and multipleOf for number properties
  • StringConstraintPlugin: Adds constraints like minLength, maxLength, pattern for string properties
  • ArrayConstraintPlugin: Adds constraints like minItems, maxItems, uniqueItems for array properties
  • ObjectConstraintPlugin: Adds constraints like minProperties, maxProperties for object properties
  • EnumConstraintPlugin: Adds enum constraints

Advanced Usage

Customizing UI Elements

You can customize the appearance or completely replace key UI elements:

import { SchemaBuilder } from "@satoshibits/react-json-schema-builder";
import type {
  NavigationProps,
  AddPropertyButtonProps,
} from "@satoshibits/react-json-schema-builder";

// Custom navigation component
const CustomNavigation = ({
  currentPath,
  onNavigateUp,
  onNavigateTo,
  isRootPath,
}: NavigationProps) => (
  <div className="my-custom-nav">
    <button onClick={onNavigateUp} disabled={isRootPath}>
      Go Back
    </button>
    <div className="breadcrumbs">
      <span onClick={() => onNavigateTo([])}>Root</span>
      {currentPath.map((path, index) => (
        <span
          key={index}
          onClick={() => onNavigateTo(currentPath.slice(0, index + 1))}
        >
          / {path}
        </span>
      ))}
    </div>
  </div>
);

// Custom add property button
const CustomAddButton = ({ onClick }: AddPropertyButtonProps) => (
  <button className="my-add-button" onClick={onClick}>
    Add New Property
  </button>
);

// Using custom components and class names
function CustomizedSchemaBuilder() {
  return (
    <SchemaBuilder
      schema={initialSchema}
      onSchemaChange={handleSchemaChange}
      // Custom class names
      backButtonClassName="my-back-button"
      propertiesListClassName="my-properties-container"
      addPropertyButtonClassName="my-add-btn"
      // Custom components
      navigationComponent={CustomNavigation}
      addPropertyButtonComponent={CustomAddButton}
    />
  );
}

Custom Property Component

You can provide a custom component to render properties:

import {
  SchemaBuilder,
  PropertyComponentProps,
} from "@satoshibits/react-json-schema-builder";

const CustomPropertyComponent = ({
  property,
  onPropertyChange,
  onKeyChange,
  onDelete,
  onDuplicate,
  onNavigate,
  disabled,
}: PropertyComponentProps) => {
  return (
    <div className="my-custom-property">
      <div className="flex justify-between">
        <h3>
          {property.key} ({property.schema.type})
        </h3>
        <div>
          {onNavigate && (
            <button
              onClick={() => onNavigate(property.key)}
              disabled={disabled}
            >
              Navigate
            </button>
          )}
          {onDuplicate && (
            <button
              onClick={() => onDuplicate(property.key)}
              disabled={disabled}
            >
              Duplicate
            </button>
          )}
          <button onClick={() => onDelete(property.key)} disabled={disabled}>
            Delete
          </button>
        </div>
      </div>
      <input
        value={property.key}
        onChange={(e) => onKeyChange(property.key, e.target.value)}
        disabled={disabled}
      />
      <input
        value={property.schema.title as string}
        onChange={(e) =>
          onPropertyChange(property.key, { title: e.target.value })
        }
        placeholder="Title"
        disabled={disabled}
      />
      {/* Add more custom inputs as needed */}
    </div>
  );
};

function CustomSchemaBuilder() {
  // ...
  return (
    <SchemaBuilder
      schema={schema}
      onSchemaChange={handleSchemaChange}
      propertyComponent={CustomPropertyComponent}
    />
  );
}

For more advanced scenarios, you can use the included context providers:

import {
  SchemaProvider,
  PluginsProvider,
  SchemaBuilder,
} from "@satoshibits/react-json-schema-builder";

function App() {
  return (
    <SchemaProvider initialSchema={initialSchema}>
      <PluginsProvider>
        <SchemaBuilder
          plugins={[numberPlugin, stringPlugin, arrayPlugin, objectPlugin]}
          onSchemaChange={handleSchemaChange}
        />
        {/* Other components that might need schema access */}
      </PluginsProvider>
    </SchemaProvider>
  );
}

Read-only Properties

You can make specific properties read-only using the disabledProperties prop:

<SchemaBuilder
  initialSchema={schema}
  onSchemaChange={handleSchemaChange}
  // These properties will be non-editable
  disabledProperties={["id", "createdAt", "updatedAt"]}
/>

Or make the entire schema builder read-only:

<SchemaBuilder
  initialSchema={schema}
  onSchemaChange={handleSchemaChange}
  disabled={true}
/>

Features in Detail

Property Management

The SchemaBuilder allows you to:

  • Add new properties with various types (string, number, integer, boolean, object, array)
  • Edit property details (key, title, description)
  • Make properties required or optional
  • Delete properties
  • Duplicate properties
  • Configure constraints based on property type

Nested Structures

You can create and navigate complex nested structures:

  • Objects with nested properties
  • Arrays with typed items (string, number, object, etc.)
  • Navigate through the schema hierarchy with breadcrumb navigation
  • Edit deeply nested properties

Constraints

With the appropriate plugins, you can configure various constraints:

  • String: minLength, maxLength, pattern, format
  • Number/Integer: minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf
  • Array: minItems, maxItems, uniqueItems
  • Object: required properties, minProperties, maxProperties

UI Customization

Customize the appearance of key UI elements:

  • Change styles of the navigation back button with backButtonClassName
  • Customize the properties container with propertiesListClassName
  • Style the Add Property button with addPropertyButtonClassName
  • Replace the entire navigation UI with a custom component via navigationComponent
  • Replace the Add Property button with a custom component via addPropertyButtonComponent
  • Customize how properties are rendered with the propertyComponent prop
  • Make specific properties read-only with disabledProperties array
  • Make the entire schema builder read-only with disabled prop

License

Apache 2.0