diff --git a/docs/core-concepts.mdx b/docs/core-concepts.mdx index f44a82c..b294341 100644 --- a/docs/core-concepts.mdx +++ b/docs/core-concepts.mdx @@ -549,39 +549,46 @@ from it. ## Micro-Frontends -Some content is not known at render time and must be loaded from another service. The v5 -`mfe()` builder embeds a _micro-frontend_: an HTML fragment fetched from a remote URL, -supporting both server-side rendering (SSR) and client-side rendering (CSR) with optional -event wiring between fragments. It requires a view built with `mfeEnabled(true)`. +A micro-frontend architecture splits a page into HTML **fragments** owned by independent services. +With v5, a _shell_ (view) page can be created with the new `mfe()` builder. +This builder embeds a micro-frontend based on metadata set in the _configuration_ object. +Each HTML fragment is fetched remotely from the URL configured in the metadata. +The composition of the micro-frontends is done by a client side +script that is required to be set in the view. +The _mfe_ feature requires a view built with `mfeEnabled(true)`. + +### Setup + +1. Build the shell view with `mfeEnabled(true)`. +2. Add the HtmlFlow MFE engine in `` element of the page. +3. Declare the micro-frontends in the view with `mfe()` method. ```java -HtmlView view = HtmlFlow.ViewFactory.builder() +static final String ENGINE = + "https://cdn.jsdelivr.net/gh/xmlet/HtmlFlow@development/htmlflow-mfe-engine/dist/htmlflow-mfe-engine.js"; + +HtmlView shell = HtmlFlow.ViewFactory.builder() .mfeEnabled(true) .build() .view(page -> page .html() - .head().__() + .head() + .script().attrType(EnumTypeScriptType.MODULE).attrSrc(ENGINE).__() + .__() // head .body() - .div() - .mfe(cfg -> { - cfg.setMfeUrlResource("http://localhost:8081/bikes"); - cfg.setMfeName("mfe1"); - cfg.setMfeElementName("some-element"); - cfg.setMfeListeningEventName("triggerBikeEvent"); - cfg.setMfeTriggersEventName("triggerCartEvent"); - cfg.setMfeScriptUrl("http://localhost:8081/js/mfe-bikes.js"); - cfg.setMfeStylingUrl("http://localhost:8081/css/style.css"); - }) - .__() // div + .mfe(cfg -> cfg + .setMfeUrlResource("http://localhost:8081/bikes/view") + .setMfeName("bikes") .__() // body .__() // html ); ``` - + + The Kotlin DSL has no sugar for `mfeEnabled`, so build the view through the same @@ -590,32 +597,299 @@ The Kotlin DSL has no sugar for `mfeEnabled`, so build the view through the same ```kotlin import htmlflow.* -val view: HtmlView<*> = HtmlFlow.ViewFactory.builder() +const val ENGINE = + "https://cdn.jsdelivr.net/gh/xmlet/HtmlFlow@development/htmlflow-mfe-engine/dist/htmlflow-mfe-engine.js" + +val shell: HtmlView<*> = HtmlFlow.ViewFactory.builder() .mfeEnabled(true) .build() .view { page -> page.html { - head { } + head { + script { attrType(EnumTypeScriptType.MODULE); attrSrc(ENGINE) } + } body { - div { - mfe { cfg -> - cfg.setMfeUrlResource("http://localhost:8081/bikes") - cfg.setMfeName("mfe1") - cfg.setMfeElementName("some-element") - cfg.setMfeListeningEventName("triggerBikeEvent") - cfg.setMfeTriggersEventName("triggerCartEvent") - cfg.setMfeScriptUrl("http://localhost:8081/js/mfe-bikes.js") - cfg.setMfeStylingUrl("http://localhost:8081/css/style.css") - } - } // div - } // body - } // html + mfe { cfg -> + cfg.setMfeUrlResource("http://localhost:8081/bikes/view") + cfg.setMfeName("bikes") + } + } + } } ``` +The engine fetches fragments and stylesheets from the browser, therefore services on another origin must +allow CORS requests from the shell. + +:::warning +When configuring a micro-frontend the url and the name are mandatory (see [MFE Configuration](#micro-frontend-configuration)). +::: +Rendering the view `shell.render()` produces the placeholder markup: + +```html + + + + + + + + +``` + +The micro-frontend mechanism expects any HTML fragments. +It can come from HtmlFlow, Thymeleaf, or any other template engine as long as HTML is provided. +Is it possible to combine server-side rendering (SSR) and client-side rendering (CSR) fragments, +an example is shown [here](#micro-frontend-configuration)). + +### Micro-frontend configuration + +The configuration object passed to the mfe method has the following properties: +| Option | Purpose | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `setMfeUrlResource(url)` **required** | URL that represents the location of HTML fragment. | +| `setMfeName(name)` **required** | Unique name of the micro-frontend, used by `window.mfe(...)` in scripts. | +| `setMfeScriptUrl(url)` | Fragment script used for hydration, added to `` as ` +``` + +If the hash script does not match the provided hash, the browser will refuse to execute it. + +:::tip Full example +[HtmlFlow-Microfrontend-Examples](https://github.com/xmlet/HtmlFlow-Microfrontend-Examples) is a +runnable webshop with a shell and bikes, cart, and order services. It demonstrates events, +reloading, shared theming, and streaming. +::: + For content that arrives **asynchronously** (reactive streams, futures, or Kotlin suspend functions) and for streaming the HTML out progressively, see [Advanced → Asynchronous Rendering](/docs/advanced#asynchronous-rendering) and