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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@jsfe/core

v0.0.5

Published

Effortless forms, with standards.

Downloads

321

Readme

📝  JSON Schema Form Element

Published on webcomponents.org

Effortless forms, with standards.

Features:

  • Instant form generation based on your JSON schemas
  • Light, and interoperable, by design
  • Integrate seamlessly with your OpenAPI / JSON schema stack
  • Comes with sensible defaults, but aims for extensibility

Why?

While there is a handful of project for major frontend frameworks, there wasn't any Web Component packing all the features above.
See also the inspirations for this project.

Warning
Not for production


Open in StackBlitz


Jump to implementations:
TypeScript onlyAstro (SSR)LitSolidVueSvelteReact



Field types

Primitives

String

title: String
required:
  - stringConstrained

properties:
  simpleString:
    title: Simple inline string
    type: string
    default: With default value from schema

  stringConstrained:
    title: String with constraints
    type: string
    pattern: '^[A-Z \d\W]+$'
    minLength: 2
    maxLength: 10
    description: Only UPPERCASE with 2 to 10 characters is allowed.

  textArea:
    title: Text area
    description: Using UI schema options.
    type: string

  color:
    title: Color picker
    type: string
    default: '#4a90e2'
# UI schema
textArea:
  'ui:widget': textarea
  'ui:placeholder': This is a placeholder
color:
  'ui:widget': color

Number

title: Number

properties:
  float:
    title: Number (float)
    type: number

  integer:
    default: 5
    title: Number (integer)
    type: integer

  numberConstrained:
    title: Number with constraints
    description: min + max + multiple of
    type: integer
    minimum: 50
    maximum: 100
    multipleOf: 10

  range:
    title: Range with default
    default: 28
    type: integer

  rangeConstrained:
    title: Range  with constraints
    type: integer
    minimum: -50
    maximum: 50
    multipleOf: 25
# UI schema
range:
  'ui:widget': range
rangeConstrained:
  'ui:widget': range

Boolean

title: Boolean

properties:
  checkbox:
    title: Checkbox (default)
    type: boolean

  switch:
    title: 'Switch, enabled by default'
    type: boolean
    default: true

  radio:
    title: Radio
    type: boolean

  radioWithDefault:
    title: 'Radio, with default'
    type: boolean
    default: false

  buttonGroup:
    title: Button group
    type: boolean
# UI schema
switch:
  'ui:widget': switch
radio:
  'ui:widget': radio
radioWithDefault:
  'ui:widget': radio
buttonGroup:
  'ui:widget': button-group

Enumeration

title: Enumeration

properties:
  select:
    title: Select menu (default)
    properties:
      string:
        title: String
        type: string
        enum: [Ola, Hello, Bonjour, Buongiorno, Guten Tag]
      number:
        title: Number
        type: number
        enum: [10, 100, 1000, 10000]
        description: With default value set
        default: 1000

  radio:
    title: Radio group
    properties:
      string:
        title: String
        type: string
        enum: [Ola, Hello, Bonjour, Buongiorno, Guten Tag]
      number:
        title: Number
        type: number
        enum: [10, 100, 1000, 10000]
        description: With default value set
        default: 1000

  buttonGroup:
    title: Button group
    properties:
      string:
        title: String
        type: string
        enum: [Ola, Hello, Bonjour, Buongiorno, Guten Tag]
        default: Ola
        description: With default value set
      number:
        title: Number
        type: number
        enum: [10, 100, 1000, 10000]
# UI schema
radio:
  string:
    'ui:widget': radio
  number:
    'ui:widget': radio

buttonGroup:
  string:
    'ui:widget': button-group
  number:
    'ui:widget': button-group

Date

title: Date and time

properties:
  datetime:
    title: Date and time
    description: Hurry up!
    type: string
    format: date-time

  date:
    title: Date
    type: string
    format: date

  time:
    title: Time
    type: string
    format: time

Object

title: Object type
description: Nests each property to a field in a fieldset.
required:
  - textBar

properties:
  textFoo:
    title: Some text input
    type: string
    description: The help text is from "description".

  textBar:
    title: Some other -required- text input
    type: string

Additional properties

🚧……🚧

Arrays

Basic

title: Basic array
type: array

items:
  properties:
    textA:
      title: Some field A
      type: string
    textB:
      title: Some field B
      type: string

Fixed

title: Fixed array
type: array

items:
  - title: A number
    type: number
    default: 42
  - title: A boolean
    type: boolean
    default: false
  - title: An object
    properties:
      when:
        title: A date
        type: string
        format: date

Nested

title: Prepopulated and nested arrays
type: array

items:
  title: Group
  type: array

  items:
    title: Some sub-field
    type: string
# Data
prepopulatedNested:
  - - Hello
    - Ola

Multiple choices (enums.)

title: A multiple choices list with checkboxes
description: Please choose yum yum.
type: array
uniqueItems: true

items:
  type: string
  enum:
    - Apple
    - Banana
    - Mango
    - Tomato
    - Baguette
    - Beaufort
    - Comté
    - Avocado

Additional items

🚧……🚧

Subschemas

allOf

🚧……🚧

oneOf

🚧……🚧

anyOf

🚧……🚧

Conditionals

Dependencies

🚧……🚧

If, then, else

🚧……🚧

Miscellaneous

References

🚧……🚧

Recursivity

🚧……🚧

Nullable values

🚧……🚧

User Interface

Schema

🚧……🚧

Usage

Installation

npm i @jsfe/core
# or
pnpm i @jsfe/core
# or
yarn add @jsfe/core
import '@jsfe/core';
import '@shoelace-style/shoelace/dist/themes/light.css';
import '@shoelace-style/shoelace/dist/themes/dark.css';

See also the CSS section.

Implementation

Note
This project is new, API is subject to changes

All examples

You can try the multi-frameworks examples like this:

npx degit https://github.com/json-schema-form-element/examples jsfe-examples

cd jsfe-examples

npm i
npm run dev

Pure HTML with CDN

See examples/src/pages/pure-html.html

-OR-

Open in CodePen.io

-OR-

Open in StackBlitz.com

TypeScript (no framework)

See examples/src/components/TypeScriptOnly.ts

-OR-

Open in StackBlitz.com

Astro (SSR)

See examples/src/components/AstroJs.astro

-OR-

Open in StackBlitz.com

Lit

See examples/src/components/LitJs.ts

-OR-

Open in StackBlitz.com

Solid

See examples/src/components/SolidJs.tsx

-OR-

Open in StackBlitz.com

Vue

See examples/src/components/VueJs.vue

-OR-

Open in StackBlitz.com

Svelte

See examples/src/components/SvelteJs.svelte

-OR-

Open in StackBlitz.com

React

See examples/src/components/ReactJs18.tsx

-OR-

Open in StackBlitz.com

CSS

Nowadays, there are many different strategies for CSS loading / bundling. JSFE is embedding its own style in its shadow, but for components libraries (here Shoelace) you should act depending on your current workflow.

References:

Shoelace is embedding styles chunks accross components, however CSS custom properties are injected globally.

TypeScript

Support for each implementation

| API | No framework | Astro (SSR) | Lit | Solid | Vue | React / Preact | Svelte | | ------------------------- | ----------------- | ----------------- | ----------------- | -------------- | ------------ | -------------- | ----------------- | | Declarative control | ✅ | ✅ | ✅ | ✅ via prop: | ✅ | ❌ | ❌ (4) | | Declarative inference | ❌ (1) | ❌ (2) | ❌ (3) | ✅ via prop: | ❌ | ❌ | ❌ | | Declarative type-checking | ❌ (1) | ❌ (2) | ✅ | ✅ via prop: | ❌ | ❌ | ❌ | | Imperative control | ✅ via DOM | - | ✅ via ref | ✅ via ref | ✅ via ref | ✅ via ref | ✅ via use: | | Imperative inference | ✅ via DOM | - | ✅ via ref | ✅ via ref | ✅ via ref | ✅ via ref | ✅ via use: | | Imperative type-checking | ✅ via DOM | - | ✅ via ref | ✅ via ref | ✅ via ref | ✅ via ref | ✅ via use: |

  1. HTML language servers can't support TypeScript obviously. But IDE can leverage Custom Element metadata.
  2. Astro JSX namespace / LSP are not handling HTMLElementTagNameMap or Custom Element metadata, yet.
  3. Template literals are preventing automatic properties inference, but at least, you can't assign wrong argument types without knowing it.
  4. Svelte heuristics are not clear regarding attributes versus properties handling. Better be safe than sorry. Also the use: directive is neat.

There might be changes regarding support for Web Components accross various the various UI frameworks above. Please file an issue if an info is wrong or missing.

Each implementation examples are trying to show off the most type-safe way to use JSFE, with the least trade-offs.

Using it more declaratively or imperatively is up to you, your framework ability and you coding style.
Bot usages are valid and can be mixed. Typically when you want to use the schema elsewhere in your app., or when your callbacks are getting too beefy, you'll better extract them from templates.

Generally, imperative usage get perfect TypeScript support (you just handle the class), whereas declaratively, you'll have to deal with various template languages limitations (this is an universal problem).

Component libraries

Shoelace

Shoelace is the UI component library of choice for rendering fields, and as a general design system backbone for JSFE.
It's beautiful, aims for simplicity, is not too opinionated, while still having character.
That's why it's the very first library implemented in JSFE.

Custom widgets

🚧……🚧

It's totally doable to swap some or all components for another system, thanks to the very Custom Element flexible nature.
First step would be to create a generic interface for communicating with individual fields, starting with the raw system browser ones as a reference. That might add a fair amount of complexity and some (negligible?) performance impact though.
Main benefit could be to add some “missing” components in Shoelace, like combobox, complex date-time ranges, or whatever fancy widget your dreaming of.

For example, React JSON Schema Form does support a handful of different UI libraries maintained by the community, but AFAIK, in the Web Component space only Shoelace is on par, thanks its Lit backbone, all while beeing totally FLOSS.
Things are changing fast though, thanks to a growing WC ecosystem, with big names backing it up (Adobe, MS, Google, IBM, SpaceX… basically everyone).

For now, the JSFE component is one Lit Element monolith. All sub-parts are “partials“, not individual Web Components. Those snippets are wrapping the Shoelace components and make them aware and alive. The validation logic / UI options are mostly happening there.
Choice has been made to tie the logic closely with the component. While this practice should be avoided generally, here we have a fully declarative / programmatic UI, so no need to create more levels of indirection than needed.
Mapping between schema and “real” fields happens at the HTMLElement level, same as all validation stuff, though you got hooks / bypasses for custom behaviors (see below).

Validation

You're responsible to hook-up additional / more advanced validation with, e.g, AJV. HTML native validation is already quite powerful, but you might want to do your own wizardry. Note that client-validation is more for user experience, while server validation is here to ensure data integrity, provide context aware round-trips…
JSON schemas are easing up the constraints enforcement for moving data around, but you'll still have to manage traditional chores.
Good news is that they give you more time to take care of business related operations, UX…

Schema massaging

Same as advanced validation handling above, JSFE doesn't bundle, dereference, nor it is fetching remote schemas.
Doing so would add a huge payload to the library, and you might certainly have already those tools at hand somewhere in your stack.
Only thing it does is resolving JSON references, pointing to local definitions only. Implementation is quite simple, and because this is a much needed feature for DRY-ness, recursivity…
Hopefully it's easy to bring in an advanced parser along, like the json-schema-ref-parser.

Custom Elements Manifests

See ./custom-elements.json & ./custom-elements.md

Experimental features

To activate experimental features preview flags, just pass the experimental property.

E.g. with Lit:

html`<json-schema-form
	otherProps="..."
	.experimental=${{
		'<flag>': true,
		// ...
	}}
></json-schema-form>`;

Actual features flags list:

  • allOf
  • oneOf

Improvements

  • BYOC (bring your own components).
  • Extensive and modern JSON Schema support (identify Draft 4 / 7 / 2020 subtleties).
  • Nice file uploaders for the data-url format.
  • Layout customizations
  • Tests, browser based (due to the WC nature).
  • Tests, tests, even more tests in the field to reveal shortcomings.
  • Support for other UI library (MWC? FAST?)
  • Drag and drop for array items, using native API.
  • Autofocuses (for added array item, etc.)
  • Have an idea? Discussions are open!

Acknowledgements

The Web Component and JSON Schema communities, the Lit team, the Shoelace maintainers,…

As a workhorse for many projects of mine for a long time, I'm grateful for all the ideas RSJF creators brought.

Similar projects:

See also:

  • remark-lint-frontmatter-schema: Validate your Markdown frontmatter data against a JSON schema.
  • retext-case-police: Check popular names casing. Example: ⚠️ github → ✅ GitHub.
  • astro-openapi: An Astro toolset for building full-stack operations easily, with type-safety and documentation as first-class citizens.