Building Web Components
Extend the platform with your own widgets — develop in your framework, bundle, upload, and register a third-party web component
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
- Clone the starter repository
- Pick a framework sample (Svelte, React, Vue, Angular, or Solid)
- Develop against the SDK with the local dev harness
- Bundle to a single ESM file
- Upload & register in the backoffice
- 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.definetag 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.
Related
- Permissions — what data your component can request
- Security model — what the sandbox allows and forbids
- Consent behavior — when end users are prompted
- Theming — platform tokens and light/dark switching