Skip to content

MicroFrontend: embed JS bundles in ZK (custom-element/mount-fn/auto), MVVM, and integration improvements#73

Merged
marioserrano09 merged 9 commits into
mainfrom
feature/microfrontend-integration
Jul 20, 2026
Merged

MicroFrontend: embed JS bundles in ZK (custom-element/mount-fn/auto), MVVM, and integration improvements#73
marioserrano09 merged 9 commits into
mainfrom
feature/microfrontend-integration

Conversation

@marioserrano09

Copy link
Copy Markdown
Contributor

Summary

Adds tools.dynamia.zk.ui.MicroFrontend, a ZK component that embeds an external JS bundle (Vue, React, Svelte, plain JS) inside a ZUL page, plus a round of integration improvements building on it.

Core component

  • Three mounting strategies: custom-element (default), mount-fn (single-spa-style mountFn/unmountFn), and auto (self-mounting SPA bundles, auto-discovered from a Vite-style dist/index.html via app=).
  • Optional CSS auto-loading (css=), Shadow DOM isolation (shadow=true), and a dynamiaEmit(data) channel injected into props so the bundle can notify the server (onMicrofrontendEvent).
  • Full ZK MVVM support: all properties are plain bean/dynamic props, so @bind/@command work out of the box, including binding whole Java POJOs (serialized to real nested JSON via Jackson, not toString()).

Integration improvements (this branch)

  • Update in place: prop-only changes (the common case once bound via MVVM) update the existing mounted instance instead of destroying and recreating it — custom-element reassigns properties, mount-fn uses an optional updateFn hook, both preserving any internal state the bundle holds.
  • Host context injection: MicroFrontendHostContextProvider beans are merged and delivered automatically as a dynamiaHost prop on every mount (tenant, locale, API base URL, auth, etc.), resolved server-side with zero client-side wiring per page.
  • Lifecycle events: onMicrofrontendReady / onMicrofrontendError (with a {message} payload) fire around mount/update, bindable with @command(...), covering script/stylesheet 404s, missing index.html, undeclared mountFn/updateFn, and exceptions from the bundle itself.
  • @dynamia-tools/microfrontend-bridge: new npm package (typecheck + 6 tests + build passing) with emitToHost/getHostContext/isDynamiaHost and TypeScript types, so bundle authors don't have to duck-type the props protocol by hand.

Demo (demo-zk-books → Library → More Examples → MicroFrontend): five panels exercising every mode, update-in-place, host context, and a button that deliberately breaks a bundle to trigger onMicrofrontendError.

Test plan

  • mvn -q -o install on platform/ui/zk — compiles clean
  • mvn -q -o compile on examples/demo-zk-books — compiles clean
  • pnpm run typecheck && pnpm run test && pnpm run build on platform/packages/microfrontend-bridge — typecheck clean, 6/6 tests pass, build produces ESM+CJS+d.ts
  • Manually verified in-browser by Mario across iterations: all four mount modes, MVVM binding/commands, POJO→JSON, shadow DOM isolation, update-in-place (no remount flicker), onMicrofrontendError demo panel

🤖 Generated with Claude Code

marioserrano09 and others added 9 commits July 20, 2026 13:49
…roying and recreating it

MVVM binding resolves each @bind-ed property with its own setter call, and every one of
them triggered a full unmount+remount, wiping any state the mounted app held (e.g. a
form the user is filling in). When only props change and the bundle/tag/mountFn/shadow
stay the same, custom-element mode now just reassigns the existing element's properties,
and mount-fn mode calls an optional updateFn(container, props) hook (single-spa-style)
instead of a full teardown; without updateFn it still falls back to unmount+remount.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…namiaHost prop

Every microfrontend needs the same cross-cutting values (tenant, locale, current user,
API base URL, auth token...), and today each ZUL author has to wire them by hand as
regular props. MicroFrontendHostContextProvider lets app code register one or more
Spring beans that get merged and injected as a "dynamiaHost" prop on every mount
(except MODE_AUTO, which has no prop channel at all), resolved entirely server-side
via Containers.findObjects() with no client-side changes needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ifecycle events

Loading/mounting failures (404 on the bundle or its stylesheet, a missing index.html
in "auto" mode, an undeclared mountFn/updateFn, an exception from the bundle itself)
previously only reached the browser console, with no way for the ZUL page to react
(loading indicator, retry button, fallback message). onMicrofrontendReady now fires
once a (re)mount/update succeeds and onMicrofrontendError fires with a {message} data
payload whenever it doesn't, both bindable with @command(...) like onMicrofrontendEvent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…pm package

Bundle authors integrating with tools.dynamia.zk.ui.MicroFrontend had to know its
ad-hoc protocol (a dynamiaEmit callback injected into props, a dynamiaHost prop with
server-registered context) and duck-type it by hand. This package wraps it in three
small, fully-typed, zero-runtime-dependency functions - emitToHost, getHostContext,
isDynamiaHost - that work the same whether the bundle is a custom element or a
mount-fn/updateFn app, following the same build/publish conventions as the existing
sdk/vue packages. Typecheck, tests (6 passing) and build verified locally.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@marioserrano09
marioserrano09 merged commit e2a8819 into main Jul 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant