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

json-schema-data-dictionary

v0.1.0

Published

Turn interlinked JSON Schema documents that describe a tabular JSON dataset into a flat, spreadsheet-like data dictionary and an embeddable, searchable HTML table.

Readme

JSON Schema → Data Dictionary

Turn one or more interlinked JSON Schema documents that describe a tabular JSON dataset (an array of objects) into:

  1. a flat, spreadsheet-like data dictionary (an array of row objects), and
  2. an embeddable, searchable HTML table for the web.

JSON Schema is a great single source of truth for biomedical / epidemiological data dictionaries — it both documents and validates the data. But it is not readable by domain experts. This library lets the schema stay authoritative while everyone sees the view they expect: data engineers read the schema, domain experts read the table.

  • Zero runtime dependencies. Ships ESM + types and a single browser bundle.
  • Resolves $ref across documents (file-position and $id-based), degrading gracefully.
  • Understands the hard cases: mixed measurement/categorical variables, sentinel/missing codes, and if/then skip patterns (structural missingness) — the bread and butter of survey data.

Install

npm install json-schema-data-dictionary

Quick start

import { schemaDocumentsToTable, renderDataDictionary, tableToHtml, toPlainRows } from "json-schema-data-dictionary";

// 1. Build the dictionary from one or more interlinked schema documents.
const table = schemaDocumentsToTable([
  { uri: "https://example.org/dataset.json", schema: datasetSchema },   // type: array
  { uri: "https://example.org/demographics.json", schema: demographics }, // a category
  { uri: "https://example.org/labs.json", schema: labs }                  // a category
]);

// 2a. Render an interactive, searchable table into any container.
renderDataDictionary(document.querySelector("#dict"), table);

// 2b. …or get a self-contained HTML string (SSR / write to a file).
const html = tableToHtml(table);

// 2c. …or export to a spreadsheet.
const rows = toPlainRows(table);              // array of plain row objects (CSV/XLSX-ready)

Pass schemas as bare objects or as { uri, name, schema }. The uri is the document's retrieval location and is used as the base for resolving its relative $refs — supply it (e.g. the file path or canonical URL) when your documents reference each other.

The output table

Each variable becomes one row with these columns:

| Column | What it holds | | --- | --- | | Variable name | The property key. | | Description | title + description + $comment (the codebook text), following $refs. | | Data type | JSON type / built-in format (date, email, uuid, …), categorical (…), array of …, or … + coded values for mixed types. | | Valid values | enum/const/oneOf/anyOf members with labels (enumDescriptions, x-enumDescriptions, or branch titles). Substantive categories are kept visually separate from special codes (missing / N/A / skip sentinels). | | Constraints | required, numeric ranges, lengths, patterns, array/object bounds, and conditional rules from skip patterns. | | Additional information | Everything else — default, examples, deprecated, vendor x-* keywords, … — as a collapsible JSON tree. |

The dataset's own title/description are shown as a header, and each externally $ref'd object schema becomes a sub-heading (e.g. Demographics, Lab measurements).

Mixed types & skip patterns

Survey variables often mix a measurement with categorical sentinel codes for structural missingness, and enforce questionnaire skip patterns with if/then. For example:

// meno_age
{
  "title": "Age at menopause (years)",
  "$comment": "Source coding: Age in years; 777 - Premenopausal; 888 - Missing/Unknown.",
  "anyOf": [
    { "type": "number", "minimum": 20, "maximum": 65, "not": { "enum": [777, 888] } },
    { "const": 777, "title": "Premenopausal at questionnaire" },
    { "$ref": "../common/defs.json#/$defs/missing3" }      // 888 = Missing/Unknown
  ]
}
// elsewhere, a row-level rule:  if meno_status = 2  then meno_age = 777

becomes a single, readable row:

Data type     number + coded values
Valid values  20–65            (measured value)
              ── special codes ──
              777  Premenopausal at questionnaire   ↳ when meno_status = 2
              888  Missing/Unknown
Constraints   Required · Measured value: 20 ≤ value ≤ 65 · When meno_status = 2, value = 777

The measurement range goes to Constraints; the codes go to Valid values, badged apart from real categories, each carrying the condition that triggers it. dependentRequired and dependentSchemas are surfaced as conditional constraints too, and all if/then rules are collected into table.conditionalRules for a dataset-level skip patterns panel.

Rendering

Interactive component (recommended)

renderDataDictionary(container, table, options?);

Mounts a <json-data-dictionary> web component. By default it uses a Shadow DOM so its styles never collide with your app. Features: instant search with highlighting + counts + empty state (/ to focus, Esc to clear), collapsible category sections, a frozen variable column, collapsible JSON trees, and copy / download-CSV.

Theme it from your page with CSS custom properties (they pierce the shadow boundary):

#dict { --dd-accent: #2563eb; }            /* also: --dd-bg, --dd-fg, --dd-border, … */

Need your app's CSS to cascade in instead? Opt out of isolation:

renderDataDictionary(container, table, { shadow: false });

Other options: { theme: "light" | "dark" | "auto", expandCategories, expandAdditionalInfo, includeExport, searchPlaceholder, emptyCell, title }.

You can also use the element directly:

<json-data-dictionary id="dict"></json-data-dictionary>
<script type="module">
  import "json-schema-data-dictionary";          // registers the element
  document.querySelector("#dict").table = table; // assign the DataDictionaryTable
</script>

Static HTML string

const html = tableToHtml(table, options?);   // inline <style> + markup + a small inline <script>

Self-contained and interactive (search/collapse/export) without any framework — good for SSR or writing a report to disk.

Spreadsheet export

toPlainRows(table)                                   // [{ "Variable name": …, … }]
toPlainRows(table, { stringifyComplexColumns: false }) // keep structured values
tableToCsv(table)                                    // RFC-4180 CSV string

Drop-in <script> (no build step)

<script src="node_modules/json-schema-data-dictionary/dist/json-schema-data-dictionary.global.js"></script>
<script>
  const { schemaDocumentsToTable, renderDataDictionary } = JsonSchemaDataDictionary;
  renderDataDictionary(document.querySelector("#dict"), schemaDocumentsToTable([/* … */]));
</script>

See examples/index.html for a live demo and examples/generate.mjs for building a static page.

API

| Export | Description | | --- | --- | | schemaDocumentsToTable(input, options?) | Build a DataDictionaryTable from schema documents. | | renderDataDictionary(container, table, options?) | Mount the interactive component; returns the element. | | tableToHtml(table, options?) | Static, self-contained HTML string. | | defineDataDictionaryElement(tag?) | Register the <json-data-dictionary> custom element. | | toPlainRows(table, options?) / tableToCsv(table, options?) | Spreadsheet export. | | buildViewModel(table, options?) | The render-ready view model (for custom UIs). | | analyzeProperty(schema, ctx) / SchemaRegistry | Lower-level building blocks. | | STRING_FORMATS, describeFormat, formatLabel | The built-in format catalog. |

Supported keywords include the full draft 2020-12 vocabulary (and draft-07 spellings): $ref/$dynamicRef, $id/$anchor, $defs/definitions, allOf/anyOf/oneOf/not, if/then/else, enum/const (+ enumDescriptions / x-enumDescriptions), every format, contentEncoding/contentMediaType, all numeric/string/array/object constraints, required/dependentRequired/dependentSchemas, and patternProperties / additionalProperties.

Develop

npm install
npm run build      # tsup -> dist/ (ESM + .d.ts + browser bundle)
npm test           # node:test over the fixtures in tests/fixtures
npm run example    # writes examples/dictionary.html

License

MIT