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

@dynamic-widget/core

v1.2.0

Published

Framework-neutral widget schema engine, layout, validation, and state.

Readme

@dynamic-widget/core

Framework-agnostic schema engine for Dynamic Widget — parse WidgetSchema, validate form state, render DOM trees, editable tabs, designer patches, charts, and the widget catalog. Use it from plain JavaScript or as the foundation for @dynamic-widget/react, @dynamic-widget/angular, and @dynamic-widget/js.

Current release: 1.2.0 — See CHANGELOG and monorepo CHANGELOG.

Live demo & docs: https://dynamic-widget-app.vercel.app/ — try the interactive demo, the visual schema designer, or read the @dynamic-widget/core API.

What's new in 1.2.0

  • Charts: seven types (bar, barHorizontal, line, area, pie, donut, gauge); legend toggle, keyboard focus, value formatting; tab designer chart data/options (tabsSetChartProps, tabsSetChartData).
  • Schema designer (headless): createSchemaDesignerController with templates (SCHEMA_DESIGNER_PRESETS), undo/redo, import merge/replace + diff summary (schemaImportSummaryLines), validation jump-to-field, plugin palette (buildPluginDashboardPaletteGroup).
  • Adapters: Angular dw-schema-designer, React SchemaDesigner, JS createSchemaDesigner — see framework READMEs.

Published as ESM ("type": "module"). Requires Node 18+ for tooling; the runtime targets modern browsers with DOM APIs.

Keywords: dynamic-widget schema-driven forms dashboard widgets validation typescript i18n async-binding options-from markdown file-upload editable-tabs wizard designer virtual-table

Install

npm install @dynamic-widget/core

Pair with @dynamic-widget/themes for CSS, and an adapter package for your framework.

Usage

import { createWidgetApi, renderDomTree } from "@dynamic-widget/core";

const host = document.getElementById("app")!;
const api = createWidgetApi(
  {
    schema: { widgets: [{ id: "name", type: "input", field: "name", label: "Name" }] },
    values: {},
    onValuesChange: (values) => console.log(values),
    onEvent: (event) => console.log(event),
  },
  () => paint()
);

const communityFlags = {
  canChart: false,
  canAdvancedWizard: false,
  canRules: false,
  canDesigner: false,
  canPlugins: false,
  canAi: false,
};

function paint() {
  renderDomTree({
    host,
    engine: api.engine,
    flags: communityFlags,
    errors: api.validate(),
    onRefresh: paint,
    onEvent: (e) => console.log(e),
  });
}

paint();

i18n (locale strings)

Use labelKey, placeholderKey, and schema.locales so one schema supports multiple languages:

import { resolveSchemaStrings, createWidgetApi } from "@dynamic-widget/core";

const api = createWidgetApi({
  schema: {
    defaultLocale: "en",
    locales: {
      en: { "form.name": "Name" },
      fr: { "form.name": "Nom" }
    },
    widgets: [{ id: "name", type: "input", field: "name", labelKey: "form.name" }]
  },
  locale: "fr"
});
// engine.getSchema().widgets[0].label === "Nom"

Or resolve before passing the schema: resolveSchemaStrings(schema, "fr"). Arabic (ar) sets dir="rtl" on the host when schema.rtl is unset. Optional: validateSchemaI18n(schema, locale) for missing keys in dev.

Async data binding (optionsFrom + dataProvider)

Load select/radio options from HTTP without per-field glue:

import { createFetchDataProvider, createWidgetApi, renderDomTree } from "@dynamic-widget/core";

const bindingCache = new Map();
const dataProvider = createFetchDataProvider();

const schema = {
  widgets: [
    {
      id: "country",
      type: "select",
      field: "country",
      label: "Country",
      optionsFrom: { url: "/api/options/countries", labelKey: "name", valueKey: "code" }
    },
    {
      id: "city",
      type: "select",
      field: "city",
      label: "City",
      visibleWhen: { field: "country", notEquals: "" },
      optionsFrom: {
        url: "/api/options/cities?country={country}",
        labelKey: "name",
        valueKey: "code",
        dependsOn: ["country"]
      }
    }
  ]
};

renderDomTree({
  host,
  engine: api.engine,
  flags: communityFlags,
  errors: [],
  onRefresh: paint,
  dataProvider,
  bindingCache
});

{field} tokens in URLs are replaced from current values. Pass a custom DataProvider for auth, GraphQL, or non-JSON backends.

Markdown (read-only, Community)

{ id: "notes", type: "markdown", label: "Release notes", props: { content: "## v1.1\n\n- **Binding** for selects" } }

Safe subset: headings, lists, bold/italic, links — no raw HTML pass-through. See renderMarkdownToElement.

File upload (Community)

{
  id: "resume",
  type: "fileUpload",
  field: "resume",
  label: "Resume",
  props: { accept: ".pdf", maxSizeBytes: 5_242_880 }
}

Emits fileSelected / fileRejected on onEvent. multiple is enterprise-gated.

Charts (enterprise)

Chart widgets render SVG via createChartSpec / renderChartToSvg when flags.canChart is true. Types: bar, barHorizontal, line, area, pie, donut, gauge. Use tabsSetChartData / tabsSetChartProps in editable tab designer events. Slice colors use --dw-chart-slice-* from @dynamic-widget/themes.

Visual schema designer (headless)

Build a custom form builder UI on the controller API (no DOM required):

import {
  createSchemaDesignerController,
  DEFAULT_FORM_DESIGNER_PALETTE,
  schemaImportSummaryLines,
} from "@dynamic-widget/core";

const ctrl = createSchemaDesignerController({ schema: mySchema });
ctrl.applyPreset("contact-form");
ctrl.addField(ctrl.createAddDraft());
ctrl.undo();
const preview = ctrl.previewImportSchemaJson(jsonText);
if (preview.ok) {
  console.log(schemaImportSummaryLines(preview.summary));
  ctrl.importSchemaJson(jsonText, "merge");
}

Ship a full UI with @dynamic-widget/angular (dw-schema-designer), @dynamic-widget/react (SchemaDesigner), or @dynamic-widget/js (createSchemaDesigner). Guide: Schema designer.

Virtual tables (enterprise)

On table widgets, set props: { virtualize: true } with >15 rows and an enterprise licence (canDesigner). Renders a scroll slice for large datasets.

validateSchema

Check structural issues before render (duplicate ids, missing type):

import { validateSchema, isSchemaValid } from "@dynamic-widget/core";

const issues = validateSchema(schema);
if (!isSchemaValid(schema)) {
  console.warn(issues);
}

Editable tabs

Handle tab designer events and apply patches to your schema:

import { applyEditableTabsAction } from "@dynamic-widget/core";

const result = applyEditableTabsAction(schema, "main-tabs", {
  type: "tabsAdd",
  tabLabel: "New tab",
});
if (result) {
  schema = result.schema;
}

Tear down

Clear the host between navigations:

host.replaceChildren();
api.refresh();

API

createWidgetApi(options, onRefresh?)

| Option | Type | Description | | --- | --- | --- | | schema | WidgetSchema | Required. Root widget tree. | | locale | string | BCP 47-ish tag; resolves labelKey / placeholderKey via schema.locales. | | dataProvider | DataProvider | Fetches JSON for optionsFrom URLs. | | values | WidgetValues | Initial field values. | | onValuesChange | (values) => void | Called when bound fields change. | | onEvent | (event) => void | Tab designer, buttons, and widget actions. | | rules | WidgetRule[] | Enterprise rules engine input. | | aiInsightProvider | AiInsightProvider | Optional async AI insight hook. |

Returns WidgetApi: getValues, setValues, setValue, validate, isValid, exportState, applyState, refresh, and engine.

renderDomTree(options)

| Option | Type | Description | | --- | --- | --- | | host | HTMLElement | Mount target (re-rendered in place). | | engine | WidgetEngine | From createWidgetApi. | | flags | EnterpriseFeatureFlags | Gated UI from @dynamic-widget/enterprise. | | errors | WidgetValidationError[] | Usually api.validate(). | | dark | boolean | Dark theme class on host. | | onRefresh | () => void | Re-paint after internal actions. | | onEvent | (event) => void | Widget / tab events. | | dataProvider | DataProvider | Required when schema uses optionsFrom. | | bindingCache | BindingCache | Per-host Map for resolved select options (adapters create one). |

Key exports

  • createFetchDataProvider, refreshBindingCache, getResolvedSelectOptions, evaluateBindingExpr — async binding
  • renderMarkdownToElement — safe markdown → DOM
  • resolveSchemaStrings, validateSchemaI18n, pickLocaleBundle, isLocaleRtl — i18n
  • createChartSpec, renderChartToSvg, chart helpers — enterprise-gated chart SVG
  • applyEditableTabsAction, widgetActionToDesignerPatch — tab layout editing
  • applyDesignerPatch, createEmptySchema — schema patches
  • createSchemaDesignerController, DEFAULT_FORM_DESIGNER_PALETTE, SCHEMA_DESIGNER_PRESETS, buildNodeFromFieldDraft, schemaImportSummaryLines, summarizeSchemaImport, buildPluginDashboardPaletteGroup — headless form designer
  • defineWidgetPlugin, registerWidgetPlugin — custom widget types (palette when canPlugins)
  • BUILTIN_WIDGET_TYPES, widget catalog helpers — v1-roadmap, features

Browser support

Requires a modern browser with standard DOM APIs (Chrome, Edge, Firefox, Safari). Chart SVG rendering and file download utilities work in evergreen desktop and mobile browsers.

@dynamic-widget/core does not assume a framework. Import it in SSR bundles only if you guard renderDomTree behind a browser check — adapters handle that pattern for React and Angular.

Related packages

License

MIT

Publishing @dynamic-widget/[email protected]

Use this checklist when cutting a monorepo release (repeat for @dynamic-widget/themes, adapters, and @dynamic-widget/enterprise with matching versions).

  1. Changelog — Confirm CHANGELOG 1.2.0 section lists chart, schema-designer, and a11y changes.
  2. Version — Set "version": "1.2.0" in package.json; adapters should depend on "@dynamic-widget/core": "^1.2.0".
  3. Build & test — From repo root: npm run build -w @dynamic-widget/core and npm test -w @dynamic-widget/core.
  4. Dry runnpm pack -w @dynamic-widget/core and inspect the tarball (dist/, README.md, CHANGELOG.md).
  5. Publishnpm publish -w @dynamic-widget/core --access public (npm OTP if enabled).
  6. Verifynpm view @dynamic-widget/core version and smoke-install in a scratch app.
  7. Adapters & themes — Publish @dynamic-widget/[email protected] first or with core, then react, angular, js, enterprise on the same tag; bump root CHANGELOG.
  8. Docs site — Deploy apps/ after packages are on npm so demos match published APIs.