npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@apart-tech/jsonforms-kit

v2.0.0

Published

A headless, bring-your-own-components library for rendering JSON Forms with any React component library

Readme

@apart-tech/jsonforms-kit

A headless, bring-your-own-components library for rendering JSON Forms with any React component library. The library handles all the heavy lifting (renderer registration, tester logic, layout dispatching, i18n integration, conditional visibility) while allowing consumers to provide their own UI components.

Features

  • Headless: No CSS or styling dependencies - bring your own components
  • Type-safe: Full TypeScript support with component contracts
  • Flexible: Works with shadcn/ui, MUI, Chakra UI, or any component library
  • Comprehensive: Supports all common form controls out of the box
  • Extensible: Easy to add custom controls and testers

Installation

npm install @apart-tech/jsonforms-kit @jsonforms/core @jsonforms/react

Quick Start

1. Create your component map

import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select';
import { Checkbox } from '@/components/ui/checkbox';

const componentMap = {
  // Required components
  Input: ({ value, onChange, type, placeholder, disabled, className, ...props }) => (
    <Input
      type={type}
      value={value || ''}
      onChange={(e) => onChange(e.target.value)}
      placeholder={placeholder}
      disabled={disabled}
      className={className}
      {...props}
    />
  ),

  Label: ({ children, className, htmlFor }) => (
    <Label className={className} htmlFor={htmlFor}>{children}</Label>
  ),

  // Optional components
  Select: ({ value, onChange, options, placeholder, disabled, className }) => (
    <Select value={value} onValueChange={onChange} disabled={disabled}>
      <SelectTrigger className={className}>
        <SelectValue placeholder={placeholder} />
      </SelectTrigger>
      <SelectContent>
        {options.map((opt) => (
          <SelectItem key={opt.value} value={opt.value}>
            {opt.label}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  ),

  Checkbox: ({ checked, onChange, disabled, className }) => (
    <Checkbox
      checked={checked}
      onCheckedChange={onChange}
      disabled={disabled}
      className={className}
    />
  ),

  // Layout components
  VerticalLayout: ({ children, className }) => (
    <div className={className || 'space-y-6'}>{children}</div>
  ),

  HorizontalLayout: ({ children, className }) => (
    <div className={className || 'flex gap-4 items-end'}>{children}</div>
  ),

  Group: ({ label, children, className }) => (
    <fieldset className={className || 'space-y-4 border rounded-lg p-4'}>
      {label && <legend className="text-lg font-semibold">{label}</legend>}
      {children}
    </fieldset>
  ),

  // Error display
  ErrorMessage: ({ message, className }) => (
    <p className={className || 'text-sm text-red-600 mt-1'}>{message}</p>
  ),

  RequiredIndicator: ({ className }) => (
    <span className={className || 'text-red-500 ml-1'}>*</span>
  ),
};

2. Create renderers

import { createRenderers } from '@apart-tech/jsonforms-kit';

const renderers = createRenderers({
  components: componentMap,
  classNames: {
    field: 'space-y-1',
    label: 'text-sm font-medium',
    error: 'text-sm text-red-600 mt-1',
    required: 'text-red-500 ml-1',
    layouts: {
      vertical: 'space-y-6',
      horizontal: 'flex gap-4 items-end',
      group: 'border rounded-lg p-4',
    },
  },
});

3. Use with JsonForms

import { useState } from 'react';
import { JsonForms } from '@jsonforms/react';
import { FormProvider } from '@apart-tech/jsonforms-kit';

function MyForm() {
  const [data, setData] = useState({});

  const schema = {
    type: 'object',
    properties: {
      name: { type: 'string', title: 'Name' },
      email: { type: 'string', format: 'email', title: 'Email' },
      role: {
        type: 'string',
        title: 'Role',
        enum: ['admin', 'user', 'guest'],
      },
    },
    required: ['name', 'email'],
  };

  return (
    <FormProvider components={componentMap}>
      <JsonForms
        schema={schema}
        data={data}
        renderers={renderers}
        onChange={({ data }) => setData(data || {})}
      />
    </FormProvider>
  );
}

Component Contracts

Each component type has a defined prop interface:

Input

interface InputProps {
  value: string;
  onChange: (value: string) => void;
  disabled?: boolean;
  placeholder?: string;
  type?: 'text' | 'email' | 'password' | 'tel' | 'url' | 'number';
  className?: string;
}

Select

interface SelectProps {
  value: string;
  onChange: (value: string) => void;
  options: Array<{ value: string; label: string }>;
  placeholder?: string;
  disabled?: boolean;
  className?: string;
}

Checkbox

interface CheckboxProps {
  checked: boolean;
  onChange: (checked: boolean) => void;
  disabled?: boolean;
  className?: string;
}

See the types documentation for all component contracts.

Supported Controls

| Control | Schema Match | Component | |---------|--------------|-----------| | String | type: "string" | Input | | Email | format: "email" | Input (type="email") | | Password | format: "password" | Input (type="password") | | URL | format: "uri" | Input (type="url") | | Phone | format: "phone" or scope ends with phone | Input (type="tel") | | Number | type: "number" or type: "integer" | Input (type="number") | | Boolean | type: "boolean" | Checkbox | | Toggle | type: "boolean" + options.format: "toggle" | Toggle | | Select | enum or oneOf | Select | | Radio | enum/boolean + options.format: "radio" | RadioGroup | | Combobox | enum + options.format: "combobox" | Combobox | | Date | format: "date" | DatePicker | | DateTime | format: "date-time" | DateTimePicker | | Textarea | string + options.multi: true | Textarea | | Slider | number + options.format: "slider" | Slider | | Currency | format: "currency" | Input (with formatting) | | File | format: "file" or contentMediaType | FileUpload |

i18n Support

import { createI18n } from '@apart-tech/jsonforms-kit';
import { useTranslations } from 'next-intl';

function useFormI18n(namespace: string) {
  const t = useTranslations(namespace);
  const tErrors = useTranslations('formErrors');

  return createI18n({
    translate: (key, defaultValue) => {
      const translated = t(key);
      return translated !== key ? translated : defaultValue;
    },
    translateError: (error) => {
      const translated = tErrors(error.keyword);
      return translated !== error.keyword ? translated : error.message;
    },
    locale: 'en',
  });
}

// Usage
function MyForm() {
  const i18n = useFormI18n('forms.personalInfo');

  return (
    <JsonForms
      // ...
      i18n={i18n}
    />
  );
}

Custom Testers

Override default tester ranks or add custom testers:

import { createRenderers, rankWith, scopeEndsWith } from '@apart-tech/jsonforms-kit';

const renderers = createRenderers({
  components: componentMap,
  testers: {
    // Override default ranks
    ranks: {
      email: 10, // Higher priority for email fields
      select: 4,
    },
    // Add custom testers
    custom: [
      {
        name: 'specialPhone',
        tester: rankWith(10, scopeEndsWith('mobileNumber')),
        renderer: 'Input',
        props: { type: 'tel' },
      },
    ],
  },
});

API Reference

createRenderers(config)

Creates JSON Forms renderer registry entries.

createI18n(config)

Creates a JSON Forms i18n configuration object.

FormProvider

React context provider for component map and class names.

Hooks

  • useComponents() - Access the component map
  • useClassNames() - Access class name configuration
  • useFieldWrapper(props) - Common field wrapper logic
  • useHasComponent(name) - Check if a component is available

License

MIT