Building Web Components

Extend the platform with your own widgets — develop in your framework, bundle, upload, and register a third-party web component

web-componentswidgetsextensibility

You can extend the platform with your own widgets: build a web component in the framework you already know, bundle it with the provided toolchain, upload it, and register it in your project's web component registry. It then appears in the Widget Catalog like any built-in widget — running inside a security sandbox, styled with the platform theme, and gated by an end-user consent overlay.

The journey at a glance

  1. Clone the starter repository
  2. Pick a framework sample (Svelte, React, Vue, Angular, or Solid)
  3. Develop against the SDK with the local dev harness
  4. Bundle to a single ESM file
  5. Upload & register in the backoffice
  6. Place the component from the widget catalog

1. Clone the starter repository

git clone https://github.com/smallstack/business-platform-web-components.git
cd business-platform-web-components
npm install
npm run build

The repository contains the bridge SDK (packages/sdk), a local dev harness (packages/dev-harness), the shared bundling toolchain (packages/build-config), and one complete sample per framework under samples/.

2. Pick a framework

Copy the sample closest to your stack — each demonstrates the full feature set (data subscription, theming, resize, graceful denied-permission states):

Sample Pattern
samples/svelte Reference template — Svelte custom element (<svelte:options customElement>)
samples/react Hand-written element wrapping a React root
samples/vue defineCustomElement — shadow-DOM reference sample
samples/angular @angular/elements, zoneless standalone component
samples/solid Hand-written element wrapping a Solid render
samples/vanilla Minimal framework-free element

Your component must register a custom element as a side effect of being imported (customElements.define("my-company-widget", …)), with a lower-case, dash-separated tag name.

3. Develop with the SDK and dev harness

All platform data arrives through @smallstack/web-component-sdk — never talk to the bridge with raw postMessage:

import { connect } from "@smallstack/web-component-sdk";

const client = connect(hostElement);
client.adoptThemeStylesheets(shadowRoot); // platform theme, live light/dark
client.observeResize(); // the host iframe sizes itself

const { grantedScopes } = await client.whenInitialized();
const profile = client.getUserProfile(); // user-profile:read
client.onData(() => {
  const orders = client.getCollection("orders"); // collection:orders:read
});

The dev harness plays the platform host locally, with production-identical sandbox isolation, mock data resolved deny-by-default from your declared permissions, a light/dark toggle, and hot reload:

npm run dev   # in your sample workspace

Mock permissions and fixtures live in the workspace's harness.config.json — see the starter repo's packages/dev-harness README for the full reference.

4. Bundle

npm run build   # emits dist/bundle.mjs

The shared toolchain produces exactly the format the platform serves: one ESM file, no externals or code-splitting, styles inlined (shadow DOM), maximum 10 MB. The bundle's SHA-256 is pinned when you upload, so builds are deterministic.

5. Upload & register in the backoffice

In your project's settings, open Web Components and create a registration:

  • Title and category — how the component appears in the widget catalog
  • Element name — must match your customElements.define tag exactly
  • Permissions — the declared scopes your component requests

Then upload dist/bundle.mjs. Every upload creates a new immutable version; the registration must have a bundle before it can be set to active. Only active registrations appear in the catalog — disabled acts as a kill switch that stops already-placed components from loading.

6. Place the component

Editors find the registration in the Widget Catalog under its category and place it on pages or dashboards. If the component declares collection scopes, the editor binds them to concrete datatypes at placement time. Before your code runs for any end user, the platform shows the consent overlay.