Widgets and view frameworks

How Beyond widgets written with React 19, Vue, Svelte or plain HTML are prepared and delivered by the CDN: packages compiled by the bundler they declare, framework controllers, styles adopted inside each widget's shadow root, and the shared stylesheet of a package.

  • Availability: Experimental
  • Evidence: Recorded run
  • How-to guide

Starting state

You have an application whose entry mounts Beyond widgets: custom elements whose view is written with React 19, Vue, Svelte or plain HTML. You want the CDN to prepare it and deliver it, and you want to know what reaches the browser.

Nothing about widgets is special to the delivery contract. A widget is a public module, delivered at the same /m/… addresses, resolved by the same documents and loaded by the same strategies as any other module. What is specific is how the modules are compiled and how their styles are applied.

What is a package, what is a controller

Part Package What it does
Widgets @beyond-js/widgets Registers each widget element, opens its shadow root, loads its module and adopts its stylesheets
Framework controllers @beyond-js/react-19-widgets, @beyond-js/vue-widgets, @beyond-js/svelte-widgets The objects that mount, hydrate, refresh and unmount a view of one framework. A plain HTML widget extends WidgetClientController of Widgets directly.
The development runtime @beyond-js/local-2026 (provisional name) Composes the internal modules of a widget and applies development updates
The frameworks react, react-dom, vue, svelte Ordinary npm packages

Your application depends on them like on any package. They are resolved into the graph of the application and pinned with it; see Registration and graph.

Each package is compiled by the bundler it declares

Widgets, the controllers and a widget package of yours declare beyond.publication with the source form and name their bundler (beyond.bundler, the ts bundler for these). The CDN compiles each of their public modules with that bundler, exactly as the development server does: .vue and .svelte components, Sass and Tailwind stylesheets and the registration of the element are produced by it, not by a generic compiler. See Publish Beyond source to npm.

The frameworks are ordinary npm packages, compiled one public module at a time. When several public subpaths of one package share internal state, as Svelte's runtime does, they are delivered so that the page holds one copy of that state. Widgets of several frameworks on one page load each framework once.

Styles stay inside each widget

A browser never applies a stylesheet because of an extension, so the delivery says who applies each one:

  • A widget adopts, inside its own shadow root, its own stylesheet and the stylesheets of the public modules it imports, transitively, stopping at another widget, which owns its own root. No rule of a widget reaches the page, and no rule of the page reaches a widget.
  • The document links only the stylesheets of modules it loads outside any widget.
  • A module selects the stylesheet of another public module with .css: import '@example/ui/theme.css'. That is a style relation, not an import of code; see Selecting an output.

The shared stylesheet of a package

A package that publishes the ./global style module has a stylesheet all of its widgets share:

JSONpackage.json
{ "exports": { "./global": "./global.css" } }

Every widget of that package adopts it inside its root, before its own sheets, so its rules can use :host and reach the widget's shadow tree. Preparing an application that uses a single widget of the package prepares the sheet too; a widget of another package does not adopt it; a package that does not publish ./global produces no request for one. See The shared stylesheet of a package.

Both loader modes, and the server

The four families render in native ES module releases and in SystemJS releases. SystemJS needs the namespace extra that each release's loader.js carries: without it, Svelte's side-effect module leaves an importer without a namespace. See SystemJS.

A widget module that also builds for Node renders on a server through its server controller. A Node.js host loads it with BEE Node, from a development server or from a release base; see Create a modular execution environment.

Rules to keep

  • One name@version per application. The runtime registers a loaded module by name, version and subpath, so an application cannot hold one version of a package from two sources.
  • Keep the widget, its controller and its view in public modules and internal files as the package declares them. A loader that rewrites a public import into a path to an internal file breaks the module the widget was compiled as.

Next

Write a widget: Author a Beyond widget. Integrate another framework: Integrate a view framework. Put one in a page that was not built with Beyond: Embed a widget in an existing page.