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

@ulb-darmstadt/shacl-form

v3.0.2

Published

SHACL form generator

Downloads

2,023

Readme

SHACL Form Generator

An HTML5 web component to edit and view RDF data that conform to SHACL shapes.

See demo here

Basic usage

<html>
  <head>
    <!-- load the bundled web component (for app development, use: npm i @ulb-darmstadt/shacl-form) -->
    <script src="https://cdn.jsdelivr.net/npm/@ulb-darmstadt/shacl-form/dist/bundle.js" type="module"></script>
  </head>
  <body>
    <!--
      Provide SHACL shapes via the data-shapes attribute
      or load them from a URL with data-shapes-url.
    -->
    <shacl-form data-shapes="
      @prefix sh:   <http://www.w3.org/ns/shacl#> .
      @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
      @prefix ex:   <http://example.org#> .

      ex:ExampleShape
        a sh:NodeShape, rdfs:Class ;
        sh:property [
          sh:name 'my value' ;
          sh:path ex:exampleValue ;
          sh:maxCount 3 ;
        ] .
    "></shacl-form>

    <script>
      const form = document.querySelector("shacl-form")
      form.addEventListener('change', event => {
        // check if form data validates according to the SHACL shapes
        if (event.detail?.valid) {
          // serialize the RDF graph and log it
          const triples = form.serialize()
          console.log('entered form data', triples)
          // store the data somewhere, e.g. in a triple store
        }
      })
    </script>
  </body>
</html>

Install and use in your project

Install the package:

npm i @ulb-darmstadt/shacl-form

This package has peer dependencies; install them in your app as well:

npm i @ro-kit/ui-widgets jsonld leaflet leaflet-editable leaflet.fullscreen n3 rdfxml-streaming-parser shacl-engine uuid

Load the web component in your app. For a Vite/webpack-style project, import it once at startup:

import '@ulb-darmstadt/shacl-form'

Then use the element in your HTML:

<shacl-form data-shapes="..."></shacl-form>

Alternatively, load the prebuilt bundle directly in a plain HTML page, as shown above:

<script src="https://cdn.jsdelivr.net/npm/@ulb-darmstadt/shacl-form/dist/bundle.js" type="module"></script>

Element attributes

Attribute | Description ---|--- data-shapes | SHACL shape definitions (e.g. Turtle) used to generate the form data-shapes-url | When data-shapes is not set, load SHACL shapes from this URL data-shape-subject | Optional subject IRI for the root node shape. If not set, the first node shape is used data-values | RDF triples (e.g. Turtle) used to prefill the form data-values-url | When data-values is not set, load RDF triples from this URL data-values-subject | Subject (IRI or blank node id) for generated data. If not set, a blank node with a new UUID is created. If data-values or data-values-url is set, this id is used to find the root node in the data graph data-values-namespace | RDF namespace used when generating new RDF subjects. Default is empty, which yields blank nodes data-values-graph | If set, serialization creates a named graph with this IRI data-language | Language for langString values (e.g. in sh:name or rdfs:label). Default is navigator.language with fallback to navigator.languages data-loading | Text displayed while the component initializes. Default is "Loading..." data-ignore-owl-imports | By default, owl:imports URLs are fetched and merged into the shapes graph. Set this attribute to disable that behavior data-view | When set, turns the component into a viewer that displays the data graph without editing data-collapse | When set, sh:groups and properties with sh:node and sh:maxCount != 1 are rendered in a collapsible accordion. Use value "open" to start expanded data-submit-button | [Ignored when data-view is set] Adds a submit button. The attribute value is used as the label. submit events fire only when the data validates data-generate-node-shape-reference | When generating RDF data, adds a triple that references the root sh:NodeShape. Default predicate is http://purl.org/dc/terms/conformsTo. Set to an empty string to disable data-show-node-ids | Show node shape subject ids in the form data-show-root-shape-label | If set and the root shape has rdfs:label or dcterms:title, display that value as a heading data-proxy | Proxy URL used when fetching resources (e.g. owl:imports). The resource URL is appended to the proxy value, e.g. http://your-proxy.org/?url= data-dense | Boolean to render a compact form with smaller paddings and margins. Default is true data-hierarchy-colors | Comma-separated list of CSS colors for nested hierarchy bars. If unset, a default palette is used data-use-shadow-root | Boolean string indicating whether <shacl-form> renders into a shadow root. Default is "true". Set to "false" to render into light DOM

Element functions

toRDF(graph?: Store): Store

Adds the form values as RDF triples to the given graph. If no graph is provided, creates a new N3 Store.

serialize(format?: string, graph?: Store): string

Serializes the given RDF graph to the given format. If no graph is provided, this calls toRDF() to construct the form data graph in one of the supported output formats (default is text/turtle).

validate(ignoreEmptyValues: boolean): Promise<boolean>

Validates form data against the SHACL shapes graph and displays validation results as icons next to the respective input fields. If ignoreEmptyValues is true, empty fields will not be marked invalid. This function is also invoked on change and submit events.

registerPlugin(plugin: Plugin)

Registers a plugin to customize editing/viewing of certain values. Plugins handle specific RDF predicates, xsd:datatypes, or both. Example: Leaflet

setTheme(theme: Theme)

Sets the design theme used for rendering. See Theming.

setClassInstanceProvider((className: string) => Promise<string>)

Sets a callback that is invoked when a SHACL property has an sh:class definition to retrieve class instances. See below for details.

setResourceLinkProvider(provider: ResourceLinkProvider)

Registers a callback provider that supplies existing resources for linking. The provider lists resources that conform to a node shape and loads RDF data for selected resources. See below.

Features

Validation

In edit mode, <shacl-form> validates the constructed data graph using shacl-engine and displays validation results as icons next to the relevant fields.

Data graph binding

<shacl-form> requires only a shapes graph as input via data-shapes (or data-shapes-url) to generate an empty form and create new RDF data from user input. Using data-values (or data-values-url) and data-values-subject, you can also bind an existing data graph to the form and prefill the fields.

Viewer mode

<shacl-form> is both an editor and a viewer. Set data-view and bind a shapes graph and a data graph to render a read-only view. See the demo.

Providing additional data to the shapes graph

Besides data-shapes and data-shapes-url, there are two ways to add RDF data to the shapes graph:

  1. While parsing the shapes graph, any owl:imports predicate with a valid HTTP URL is fetched. The response is parsed (using one of the supported MIME types) and added to a named graph. This graph is scoped to the node where the owl:import is defined and its sub nodes.

    The example shapes graph contains the following triples:

    example:Attribution
      sh:property [
        owl:imports <https://w3id.org/nfdi4ing/metadata4ing/> ;
        sh:name      "Role" ;
        sh:path      dcat:hadRole ;
        sh:class     prov:Role ;
      ] .

    In this case, the URL references an ontology that defines instances of prov:Role. These instances populate the "Role" dropdown. The imported ontology is available only for rendering and validating this specific property.

  2. The <shacl-form> element exposes setClassInstanceProvider((className: string) => Promise<string>), a callback invoked when a property has sh:class. The return value is a string (e.g. text/turtle) containing instance definitions of the given class.

    In this example, the code:

    form.setClassInstanceProvider((clazz) => {
      if (clazz === 'http://example.org/Material') {
        return `
          <http://example.org/steel>   a <http://example.org/Material>; <http://www.w3.org/2000/01/rdf-schema#label> "Steel".
          <http://example.org/wood>    a <http://example.org/Material>; <http://www.w3.org/2000/01/rdf-schema#label> "Wood".
          <http://example.org/alloy>   a <http://example.org/Material>; <http://www.w3.org/2000/01/rdf-schema#label> "Alloy".
          <http://example.org/plaster> a <http://example.org/Material>; <http://www.w3.org/2000/01/rdf-schema#label> "Plaster".
        `
      }
    })

    returns instances of http://example.org/Material, which populate the "Artwork material" dropdown.

    A more realistic use case is calling an API endpoint to fetch class instances from existing ontologies.

Use of SHACL sh:class

When a property shape has an sh:class, all available graphs are scanned for instances of that class so users can choose from them. rdfs:subClassOf is also considered when building the list of class instances.

shacl-form also supports class instance hierarchies modelled with skos:broader and/or skos:narrower. This is illustrated by the "Subject classification" property in the example.

SHACL constraints sh:or and sh:xone

<shacl-form> supports sh:or and sh:xone to let users choose between different options on nodes or properties. The example shapes graph includes:

example:Attribution
  a sh:NodeShape ;
  sh:property [
    sh:maxCount  1 ;
    sh:minCount  1 ;
    sh:path prov:agent ;
    sh:or (
      [ sh:node example:Person ; rdfs:label "Person" ]
      [ sh:node example:Organisation ; rdfs:label "Organisation" ]
    )
  ] .

When adding a new attribution, <shacl-form> renders a dropdown to select Person/Organisation. After selection, the dropdown is replaced by the input fields of the chosen node shape.

When binding an existing data graph to the form, the constraint is resolved based on the data value:

  • For RDF literals, an sh:or option with a matching sh:datatype is chosen
  • For blank nodes or named nodes, the rdf:type is matched with a node shape having a corresponding sh:targetClass or with a property shape having a corresponding sh:class. If there is no rdf:type but a sh:nodeKind of sh:IRI, the node id is used as the value

Linking existing data

When a node shape has a sh:targetClass and any graph contains instances of that class, those instances can be linked in the respective SHACL property. The generated data graph will then contain only a reference to the instance, not its full triples.

Graphs considered are:

If your graphs only contain resource identifiers (IRIs) and not the full triples for linked resources, you can use setResourceLinkProvider to supply them on demand. The ResourceLinkProvider lets you:

  • List resources that conform to a node shape so they can appear in the "Link existing ..." dialog.
  • Load RDF data for selected resource IRIs so the shacl-form can resolve, display, and validate linked resources.

The provider supports eager loading (resolve resources during initialization) or lazy loading (resolve when the user opens the link dialog). See here for an example implementation.

SHACL shape inheritance

SHACL defines two ways of inheriting shapes: sh:and and sh:node. <shacl-form> supports both. In this example, node shape example:ArchitectureModelDataset extends example:Dataset by defining:

example:ArchitectureModelDataset sh:node example:Dataset .

Properties of inherited shapes are displayed first.

Plugins

Plugins can modify rendering and add edit/view functionality for specific RDF datatypes or predicates (or both). For example, the JavaScript on this page includes:

import { LeafletPlugin } from '@ulb-darmstadt/shacl-form/plugins/leaflet.js'
const form = document.getElementById("shacl-form")
form.registerPlugin(new LeafletPlugin({ datatype: 'http://www.opengis.net/ont/geosparql#wktLiteral' }))

When a SHACL property has datatype http://www.opengis.net/ont/geosparql#wktLiteral, the plugin renders the editor/viewer elements. This plugin uses Leaflet to edit or view geometry in well known text on a map.

Custom plugins can be built by extending Plugin.

Property grouping and collapsing

Properties can be grouped using sh:group in the shapes graph. This example defines a group "Physical properties" and assigns certain properties to it.

When data-collapse is set, <shacl-form> creates an accordion-like widget that toggles grouped properties to reduce visual complexity. If the grouped properties should start open, set data-collapse="open".

In addition, all properties with sh:node and sh:maxCount != 1 are collapsed.

Supported RDF formats

Input formats

Output formats

  • text/turtle, application/n-triples, application/n-quads, application/trig using the N3 writer
  • application/ld+json using jsonld

Theming

<shacl-form> has a built-in abstraction layer for theming the form controls. To use another theme (e.g. Bootstrap or Material Design), extend Theme and call setTheme() on the element.

If you only want to restyle the existing widgets (without re-implementing internal behavior), you can use CSS in two ways:

  1. Render into light DOM for global CSS:
<shacl-form data-use-shadow-root="false"></shacl-form>
  1. Use CSS variables and parts (works with Shadow DOM too). The following CSS variables are supported:
shacl-form {
  --shacl-font-family: system-ui, sans-serif;
  --shacl-font-size: 14px;
  --shacl-text-color: #333;
  --shacl-muted-color: #555;
  --shacl-border-color: #ddd;
  --shacl-bg: #fff;
  --shacl-row-alt-bg: #f8f8f8;
  --shacl-error-color: #c00;
  --shacl-label-width: 10em;
}

And these parts are exposed for styling:

shacl-form::part(form) { padding: 8px; }
shacl-form::part(field) { gap: 6px; }
shacl-form::part(label) { font-weight: 600; }
shacl-form::part(editor) { border-radius: 6px; }
shacl-form::part(button) { min-height: 32px; }
shacl-form::part(primary) { font-weight: 700; }
shacl-form::part(add-button) { }
shacl-form::part(remove-button) { }
shacl-form::part(link-button) { }
shacl-form::part(submit-button) { }

Available parts: form, node, linked-node, node-title, group, group-title, collapsible, property, property-instance, field, label, editor, lang-chooser, constraint, constraint-editor, add-controls, remove-controls, add-button, remove-button, link-button, submit-button, button, primary.

Note: the default widgets are provided by ULB Darmstadt. Those components expose their own part names; you can style them via ::part(...) selectors on the respective elements. See the README for documentation.

Use with Solid Pods

<shacl-form> can be integrated with Solid Pods. Because toRDF() returns an RDF/JS N3 Store (see above), it can be passed to the Solid client fromRdfJsDataset() function to convert it into a Solid Dataset. The example below (based on Inrupt's Solid Pod tutorial) shows how to merge data from a <shacl-form> with a Solid data resource at readingListDataResourceURI:

  // Authentication is assumed, resulting in a fetch function able to read and write into the Pod
  try {
    // Get data out of the shacl-form
    const form = document.querySelector('shacl-form')

    // Extract the RDF graph from the form
    const shaclFormGraphStore = await form.toRDF()

    // Convert RDF store into a Solid dataset
    const shaclFormDataset = await fromRdfJsDataset(shaclFormGraphStore)

    // First get the current dataset
    myReadingList = await getSolidDataset(readingListDataResourceURI, { fetch: fetch })

    // get all things from the shaclFormDataset
    const shaclFormThings = getThingAll(shaclFormDataset)

    // add the things from ShaclForm to the existing set
    shaclFormThings.forEach((thing) => (myReadingList = setThing(myReadingList, thing)))

    // save the new dataset
    let savedReadingList = await saveSolidDatasetAt(readingListDataResourceURI, myReadingList, {
      fetch: fetch
    })

    // Other handling here

  } catch (err) {
    console.error(`Storing SHACL data from Form failed with error ${err}!`)
  }