
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Tue, 14 Apr 2026 18:58:58 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Building vertical microfrontends on Cloudflare’s platform]]></title>
            <link>https://blog.cloudflare.com/vertical-microfrontends/</link>
            <pubDate>Fri, 30 Jan 2026 14:00:00 GMT</pubDate>
            <description><![CDATA[ Deploy multiple Workers under a single domain with the ability to make them feel like single-page applications. We take a look at how service bindings enable URL path routing to multiple projects. ]]></description>
            <content:encoded><![CDATA[ <p><i>Updated at 6:55 a.m. PT</i></p><p>Today, we’re introducing a new Worker template for Vertical Microfrontends (VMFE). <a href="https://dash.cloudflare.com/?to=/:account/workers-and-pages/create?type=vmfe"><u>This template</u></a> allows you to map multiple independent <a href="https://workers.cloudflare.com/"><u>Cloudflare Workers</u></a> to a single domain, enabling teams to work in complete silos — shipping marketing, docs, and dashboards independently — while presenting a single, seamless application to the user.</p><a href="https://dash.cloudflare.com/?to=/:account/workers-and-pages/create?type=vmfe"><img src="https://deploy.workers.cloudflare.com/button" /></a>
<p></p><p>Most microfrontend architectures are "horizontal", meaning different <i>parts</i> of a single page are fetched from different services. Vertical microfrontends take a different approach by splitting the application by URL path. In this model, a team owning the `/blog` path doesn't <i>just</i> own a component; they own the entire vertical stack for that route – framework, library choice, CI/CD and more. Owning the entire stack of a path, or set of paths, allows teams to have true ownership of their work and ship with confidence.</p><p>Teams face problems as they grow, where different frameworks serve varying use cases. A marketing website could be better utilized with Astro, for example, while a dashboard might be better with React. Or say you have a monolithic code base where many teams ship as a collective. An update to add new features from several teams can get frustratingly rolled back because a single team introduced a regression. How do we solve the problem of obscuring the technical implementation details away from the user and letting teams ship a cohesive user experience with full autonomy and control of their domains?</p><p>Vertical microfrontends can be the answer. Let’s dive in and explore how they solve developer pain points together.</p>
    <div>
      <h2>What are vertical microfrontends?</h2>
      <a href="#what-are-vertical-microfrontends">
        
      </a>
    </div>
    <p>A vertical microfrontend is an architectural pattern where a single independent team owns an entire slice of the application’s functionality, from the user interface all the way down to the <a href="https://www.cloudflare.com/learning/serverless/glossary/what-is-ci-cd/">CI/CD pipeline</a>. These slices are defined by paths on a domain where you can associate individual Workers with specific paths:</p>
            <pre><code>/      = Marketing
/docs  = Documentation
/blog  = Blog
/dash  = Dashboard</code></pre>
            <p>We could take it a step further and focus on more granular sub-path Worker associations, too, such as a dashboard. Within a dashboard, you likely segment out various features or products by adding depth to your URL path (e.g. <code>/dash/product-a</code>) and navigating between two products could mean two entirely different code bases. </p><p>Now with vertical microfrontends, we could also have the following:</p>
            <pre><code>/dash/product-a  = WorkerA
/dash/product-b  = WorkerB</code></pre>
            <p>Each of the above paths are their own frontend project with zero shared code between them. The <code>product-a</code> and <code>product-b</code> routes map to separately deployed frontend applications that have their own frameworks, libraries, CI/CD pipelines defined and owned by their own teams. FINALLY.</p><p>You can now own your own code from end to end. But now we need to find a way to stitch these separate projects together, and even more so, make them feel as if they are a unified experience.</p><p>We experience this pain point ourselves here at Cloudflare, as the dashboard has many individual teams owning their own products. Teams must contend with the fact that changes made outside their control impact how users experience their product. </p><p>Internally, we are now using a similar strategy for our own dashboard. When users navigate from the core dashboard into our ZeroTrust product, in reality they are two entirely separate projects and the user is simply being routed to that project by its path <code>/:accountId/one</code>.</p>
    <div>
      <h2>Visually unified experiences</h2>
      <a href="#visually-unified-experiences">
        
      </a>
    </div>
    <p>Stitching these individual projects together to make them feel like a unified experience isn’t as difficult as you might think: It only takes a few lines of CSS magic. What we <i>absolutely do not want</i> to happen is to leak our implementation details and internal decisions to our users. If we fail to make this user experience feel like one cohesive frontend, then we’ve done a grave injustice to our users. </p><p>To accomplish this sleight of hand, let us take a little trip in understanding how view transitions and document preloading come into play.</p>
    <div>
      <h3>View transitions</h3>
      <a href="#view-transitions">
        
      </a>
    </div>
    <p>When we want to seamlessly navigate between two distinct pages while making it feel smooth to the end user, <a href="https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API"><u>view transitions</u></a> are quite useful. Defining specific <a href="https://www.w3schools.com/jsref/dom_obj_all.asp"><u>DOM elements</u></a> on our page to stick around until the next page is visible, and defining how any changes are handled, make for quite the powerful quilt-stitching tool for multi-page applications.</p><p>There may be, however, instances where making the various vertical microfrontends feel different is more than acceptable. Perhaps our marketing website, documentation, and dashboard are each uniquely defined, for instance. A user would not expect all three of those to feel cohesive as you navigate between the three parts. But… if you decide to introduce vertical slices to an individual experience such as the dashboard (e.g. <code>/dash/product-a</code> &amp; <code>/dash/product-b</code>), then users should <b>never</b> know they are two different repositories/workers/projects underneath.</p><p>Okay, enough talk — let’s get to work. I mentioned it was low-effort to make two separate projects feel as if they were one to a user, and if you have yet to hear about <a href="https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API"><u>CSS View Transitions</u></a> then I’m about to blow your mind.</p><p>What if I told you that you could make animated transitions between different views  — single-page app (SPA) or multi-page app (MPA) — feel as if they were one? Before any view transitions are added, if we navigate between pages owned by two different Workers, the interstitial loading state would be the white blank screen in our browser for some few hundred milliseconds until the full next page began rendering. Pages would not feel cohesive, and it certainly would not feel like a single-page application.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4vw1Am7gYUQPtmFFCRcsu1/774b881dff7ce1c26db88f30623dfc13/image3.png" />
          </figure><p><sup>Appears as multiple navigation elements between each site.</sup></p><p>If we want elements to stick around, rather than seeing a white blank page, we can achieve that by defining CSS View Transitions. With the code below, we’re telling our current document page that when a view transition event is about to happen, keep the <code>nav</code> DOM element on the screen, and if any delta in appearance exists between our existing page and our destination page, then we’ll animate that with an <code>ease-in-out</code> transition.</p><p>All of a sudden, two different Workers feel like one.</p>
            <pre><code>@supports (view-transition-name: none) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 0.3s;
    animation-timing-function: ease-in-out;
  }
  nav { view-transition-name: navigation; }
}</code></pre>
            
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4h6Eh5LSX4552QJDvV1l7o/a5a43ee0e6e011bca58ecc2d74902744/image1.png" />
          </figure><p><sup>Appears as a single navigation element between three distinct sites.</sup></p>
    <div>
      <h3>Preloading</h3>
      <a href="#preloading">
        
      </a>
    </div>
    <p>Transitioning between two pages makes it <i>look</i> seamless — and we also want it to <i>feel</i> as instant as a client-side SPA. While currently Firefox and Safari do not support <a href="https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API"><u>Speculation Rules</u></a>, Chrome/Edge/Opera do support the more recent newcomer. The speculation rules API is designed to improve performance for future navigations, particularly for document URLs, making multi-page applications feel more like single-page applications.</p><p>Breaking it down into code, what we need to define is a script rule in a specific format that tells the supporting browsers how to prefetch the other vertical slices that are connected to our web application — likely linked through some shared navigation.</p>
            <pre><code>&lt;script type="speculationrules"&gt;
  {
    "prefetch": [
      {
        "urls": ["https://product-a.com", "https://product-b.com"],
        "requires": ["anonymous-client-ip-when-cross-origin"],
        "referrer_policy": "no-referrer"
      }
    ]
  }
&lt;/script&gt;</code></pre>
            <p>With that, our application prefetches our other microfrontends and holds them in our in-memory cache, so if we were to navigate to those pages it would feel nearly instant.</p><p>You likely won’t require this for clearly discernible vertical slices (marketing, docs, dashboard) because users would expect a slight load between them. However, it is highly encouraged to use when vertical slices are defined within a specific visible experience (e.g. within dashboard pages).</p><p>Between <a href="https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API"><u>View Transitions</u></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API"><u>Speculation Rules</u></a>, we are able to tie together entirely different code repositories to feel as if they were served from a single-page application. Wild if you ask me.</p>
    <div>
      <h2>Zero-config request routing</h2>
      <a href="#zero-config-request-routing">
        
      </a>
    </div>
    <p>Now we need a mechanism to host multiple applications, and a method to stitch them together as requests stream in. Defining a single Cloudflare Worker as the “Router” allows a single logical point (at the edge) to handle network requests and then forward them to whichever vertical microfrontend is responsible for that URL path. Plus it doesn’t hurt that then we can map a single domain to that router Worker and the rest “just works.”</p>
    <div>
      <h3>Service bindings</h3>
      <a href="#service-bindings">
        
      </a>
    </div>
    <p>If you have yet to explore Cloudflare Worker <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/"><u>service bindings</u></a>, then it is worth taking a moment to do so. </p><p>Service bindings allow one Worker to call into another, without going through a publicly-accessible URL. A Service binding allows Worker A to call a method on Worker B, or to forward a request from Worker A to Worker. Breaking it down further, the Router Worker can call into each vertical microfrontend Worker that has been defined (e.g. marketing, docs, dashboard), assuming each of them were Cloudflare Workers.</p><p>Why is this important? This is precisely the mechanism that “stitches” these vertical slices together. We’ll dig into how the request routing is handling the traffic split in the next section. But to define each of these microfrontends, we’ll need to update our Router Worker’s wrangler definition, so it knows which frontends it’s allowed to call into.
</p>
            <pre><code>{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "router",
  "main": "./src/router.js",
  "services": [
    {
      "binding": "HOME",
      "service": "worker_marketing"
    },
    {
      "binding": "DOCS",
      "service": "worker_docs"
    },
    {
      "binding": "DASH",
      "service": "worker_dash"
    },
  ]
}</code></pre>
            <p>Our above sample definition is defined in our Router Worker, which then tells us that we are permitted to make requests into three separate additional Workers (marketing, docs, and dash). Granting permissions is as simple as that, but let’s tumble into some of the more complex logic with request routing and HTML rewriting network responses.</p>
    <div>
      <h3>Request routing</h3>
      <a href="#request-routing">
        
      </a>
    </div>
    <p>With knowledge of the various other Workers we are able to call into if needed, now we need some logic in place to know where to direct network requests when. Since the Router Worker is assigned to our custom domain, all incoming requests hit it first at the network edge. It then determines which Worker should handle the request and manages the resulting response. </p><p>The first step is to map URL paths to associated Workers. When a certain request URL is received, we need to know where it needs to be forwarded. We do this by defining rules. While we support wildcard routes, dynamic paths, and parameter constraints, we are going to stay focused on the basics — literal path prefixes — as it illustrates the point more clearly. </p><p> In this example, we have three microfrontends:</p>
            <pre><code>/      = Marketing
/docs  = Documentation
/dash  = Dashboard</code></pre>
            <p>Each of the above paths need to be mapped to an actual Worker (see our wrangler definition for services in the section above). For our Router Worker, we define an additional variable with the following data, so we can know which paths should map to which service bindings. We now know where to route users as requests come in! Define a wrangler variable with the name ROUTES and the following contents:</p>
            <pre><code>{
  "routes":[
    {"binding": "HOME", "path": "/"},
    {"binding": "DOCS", "path": "/docs"},
    {"binding": "DASH", "path": "/dash"}
  ]
}</code></pre>
            <p>Let’s envision a user visiting our website path <code>/docs/installation</code>. Under the hood, what happens is the request first reaches our Router Worker which is in charge of understanding what URL paths map to which individual Workers. It understands that the <code>/docs</code> path prefix is mapped to our <code>DOCS</code> service binding which referencing our wrangler file points us at our <code>worker_docs</code> project. Our Router Worker, knowing that <code>/docs</code> is defined as a vertical microfrontend route, removes the <code>/docs</code> prefix from the path and forwards the request to our <code>worker_docs</code> Worker to handle the request and then finally returns whatever response we get.</p><p>Why does it drop the <code>/docs</code> path, though? This was an implementation detail choice that was made so that when the Worker is accessed via the Router Worker, it can clean up the URL to handle the request <i>as if </i>it were called from outside our Router Worker. Like any Cloudflare Worker, our <code>worker_docs</code> service might have its own individual URL where it can be accessed. We decided we wanted that service URL to continue to work independently. When it’s attached to our new Router Worker, it would automatically handle removing the prefix, so the service could be accessible from its own defined URL or through our Router Worker… either place, doesn’t matter.</p>
    <div>
      <h3>HTMLRewriter</h3>
      <a href="#htmlrewriter">
        
      </a>
    </div>
    <p>Splitting our various frontend services with URL paths (e.g. <code>/docs</code> or <code>/dash</code>) makes it easy for us to forward a request, but when our response contains HTML that doesn’t know it’s being reverse proxied through a path component… well, that causes problems. </p><p>Say our documentation website has an image tag in the response <code>&lt;img src="./logo.png" /&gt;</code>. If our user was visiting this page at <code>https://website.com/docs/</code>, then loading the <code>logo.png</code> file would likely fail because our <code>/docs</code> path is somewhat artificially defined only by our Router Worker.</p><p>Only when our services are accessed through our Router Worker do we need to do some HTML rewriting of absolute paths so our returned browser response references valid assets. In practice what happens is that when a request passes through our Router Worker, we pass the request to the correct Service Binding, and we receive the response from that. Before we pass that back to the client, we have an opportunity to rewrite the DOM — so where we see absolute paths, we go ahead and prepend that with the proxied path. Where previously our HTML was returning our image tag with <code>&lt;img src="./logo.png" /&gt;</code> we now modify it before returning to the client browser to <code>&lt;img src="./docs/logo.png" /&gt;</code>.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/10jKx6qt2YcarpDyEsFNYV/3b0f11f56e3c9b2deef59934cf8efa7f/image2.png" />
          </figure><p>Let’s return for a moment to the magic of CSS view transitions and document preloading. We could of course manually place that code into our projects and have it work, but this Router Worker will <i>automatically</i> handle that logic for us by also using <a href="https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/"><u>HTMLRewriter</u></a>. </p><p>In your Router Worker <code>ROUTES</code> variable, if you set <code>smoothTransitions</code> to <code>true</code> at the root level, then the CSS transition views code will be added automatically. Additionally, if you set the <code>preload</code> key within a route to <code>true</code>, then the script code speculation rules for that route will automatically be added as well. </p><p>Below is an example of both in action:</p>
            <pre><code>{
  "smoothTransitions":true, 
  "routes":[
    {"binding": "APP1", "path": "/app1", "preload": true},
    {"binding": "APP2", "path": "/app2", "preload": true}
  ]
}</code></pre>
            
    <div>
      <h2>Get started</h2>
      <a href="#get-started">
        
      </a>
    </div>
    <p>You can start building with the Vertical Microfrontend template today.</p><p>Visit the Cloudflare Dashboard <a href="https://dash.cloudflare.com/?to=/:account/workers-and-pages/create?type=vmfe"><u>deeplink here</u></a> or go to “Workers &amp; Pages” and click the “Create application” button to get started. From there, click “Select a template” and then “Create microfrontend” and you can begin configuring your setup.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1teTcTNHzQH3yCvbTz3xyU/8f9a4b2ef3ec1c6ed13cbdc51d6b13c5/image5.png" />
          </figure><p>
Check out the <a href="https://developers.cloudflare.com/workers/framework-guides/web-apps/microfrontends"><u>documentation</u></a> to see how to map your existing Workers and enable View Transitions. We can't wait to see what complex, multi-team applications you build on the edge!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Dashboard]]></category>
            <category><![CDATA[Front End]]></category>
            <category><![CDATA[Micro-frontends]]></category>
            <guid isPermaLink="false">2u7SNZ4BZcQYHZYKqmdEaM</guid>
            <dc:creator>Brayden Wilmoth</dc:creator>
        </item>
        <item>
            <title><![CDATA[Integrating Turnstile with the Cloudflare WAF to challenge fetch requests]]></title>
            <link>https://blog.cloudflare.com/integrating-turnstile-with-the-cloudflare-waf-to-challenge-fetch-requests/</link>
            <pubDate>Mon, 18 Dec 2023 14:00:17 GMT</pubDate>
            <description><![CDATA[ By editing or creating a new Turnstile widget with “Pre-Clearance” enabled, Cloudflare customers can now use Turnstile to issue a challenge when a page’s HTML loads, and enforce that all valid responses have a valid Turnstile token ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3UV6CnIMI92jBmCr4VeqCU/98b0de9d9ca221f3d60bc7d02213264c/image8.png" />
            
            </figure><p>Two months ago, we made Cloudflare Turnstile <a href="/turnstile-ga/">generally available</a> — giving website owners everywhere an easy way to fend off bots, without ever issuing a CAPTCHA. Turnstile allows any website owner to embed a frustration-free Cloudflare challenge on their website with a simple code snippet, making it easy to help ensure that only human traffic makes it through. In addition to protecting a website’s frontend, Turnstile also empowers web administrators to harden browser-initiated (AJAX) API calls running under the hood. These APIs are commonly used by dynamic single-page web apps, like those created with React, Angular, Vue.js.</p><p>Today, we’re excited to announce that we have integrated Turnstile with the <a href="https://www.cloudflare.com/application-services/products/waf/">Cloudflare Web Application Firewall (WAF)</a>. This means that web admins can add the Turnstile code snippet to their websites, and then configure the Cloudflare WAF to manage these requests. This is completely customizable using WAF Rules; for instance, you can allow a user authenticated by Turnstile to interact with all of an application’s API endpoints without facing any further challenges, or you can configure certain sensitive endpoints, like Login, to always issue a challenge.</p>
    <div>
      <h3>Challenging fetch requests in the Cloudflare WAF</h3>
      <a href="#challenging-fetch-requests-in-the-cloudflare-waf">
        
      </a>
    </div>
    <p>Millions of websites protected by Cloudflare’s WAF leverage our JS Challenge, Managed Challenge, and Interactive Challenge to stop bots while letting humans through. For each of these challenges, Cloudflare intercepts the matching request and responds with an HTML page rendered by the browser, where the user completes a basic task to demonstrate that they’re human. When a user successfully completes a challenge, they receive a <a href="https://developers.cloudflare.com/fundamentals/reference/policies-compliances/cloudflare-cookies/#additional-cookies-used-by-the-challenge-platform">cf_clearance cookie</a>, which tells Cloudflare that a user has successfully passed a challenge, the type of challenge, and when it was completed. A clearance cookie can’t be shared between users, and is only valid for the time set by the Cloudflare customer in their Security Settings dashboard.</p><p>This process works well, except when a browser receives a challenge on a fetch request and the browser has not previously passed a challenge. On a fetch request, or an XML HTTP Request (XHR), the browser expects to get back simple text (in JSON or XML formats) and cannot render the HTML necessary to run a challenge.</p><p>As an example, let’s imagine a pizzeria owner who built an online ordering form in React with a payment page that submits data to an API endpoint that processes payments. When a user views the web form to add their credit card details they can pass a Managed Challenge, but when the user submits their credit card details by making a fetch request, the browser won’t execute the code necessary for a challenge to run. The pizzeria owner’s only option for handling suspicious (but potentially legitimate) requests is to block them, which runs the risk of false positives that could cause the restaurant to lose a sale.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7fOg2KPmEgB5nyeywCc0X0/8ddf84d382f902ad633fb30a3f8226a2/Group-3955.png" />
            
            </figure><p>This is where Turnstile can help. Turnstile allows anyone on the Internet to embed a Cloudflare challenge anywhere on their website. Before today, the output of Turnstile was only a one-time use token. To enable customers to issue challenges for these fetch requests, Turnstile can now issue a clearance cookie for the domain that it's embedded on. Customers can issue their challenge within the HTML page before a fetch request, <i>pre-clearing</i> the visitor to interact with the Payment API.</p>
    <div>
      <h3>Turnstile Pre-Clearance mode</h3>
      <a href="#turnstile-pre-clearance-mode">
        
      </a>
    </div>
    <p>Returning to our pizzeria example, the three big advantages of using Pre-Clearance to integrate Turnstile with the Cloudflare WAF are:</p><ol><li><p><b>Improved user experience</b>: Turnstile’s embedded challenge can run in the background while the visitor is entering their payment details.</p></li><li><p><b>Blocking more requests at the edge</b>: Because Turnstile now issues a clearance cookie for the domain that it’s embedded on, our pizzeria owner can use a Custom Rule to issue a Managed Challenge for every request to the payment API. This ensures that automated attacks attempting to target the payment API directly are stopped by Cloudflare before they can reach the API.</p></li><li><p><b>(Optional) Securing the action and the user</b>: No backend code changes are necessary to get the benefit of Pre-Clearance. However, further Turnstile integration will increase security for the integrated API. The pizzeria owner can adjust their payment form to <a href="https://developers.cloudflare.com/turnstile/get-started/server-side-validation/">validate the received Turnstile token</a>, ensuring that every payment attempt is individually validated by Turnstile to protect their payment endpoint from session hijacking.</p></li></ol>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Er3Qa9TvxheeCOxbYeCQh/50afffa59cef839aba3a256484ea6ea5/Pre-clearance.png" />
            
            </figure><p>A Turnstile widget with Pre-Clearance enabled will still issue turnstile tokens, which gives customers the flexibility to decide if an endpoint is critical enough to require a security check on every request to it, or just once a session. Clearance cookies issued by a Turnstile widget are automatically applied to the Cloudflare zone the Turnstile widget is embedded on, with no configuration necessary. The clearance time the token is valid for is still controlled by the zone specific “Challenge Passage” time.</p>
    <div>
      <h3>Implementing Turnstile with Pre-Clearance</h3>
      <a href="#implementing-turnstile-with-pre-clearance">
        
      </a>
    </div>
    <p>Let’s make this concrete by walking through a basic implementation. Before we start, we’ve set up a simple demo application where we emulate a frontend talking to a backend on a <code>/your-api</code> endpoint.</p><p>To this end, we have the following code:</p>
            <pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
   &lt;title&gt;Turnstile Pre-Clearance Demo &lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;main class="pre-clearance-demo"&gt;
    &lt;h2&gt;Pre-clearance Demo&lt;/h2&gt;
    &lt;button id="fetchBtn"&gt;Fetch Data&lt;/button&gt;
    &lt;div id="response"&gt;&lt;/div&gt;
&lt;/main&gt;

&lt;script&gt;
  const button = document.getElementById('fetchBtn');
  const responseDiv = document.getElementById('response');
  button.addEventListener('click', async () =&gt; {
  try {
    let result = await fetch('/your-api');
    if (result.ok) {
      let data = await result.json();
      responseDiv.textContent = JSON.stringify(data);
    } else {
      responseDiv.textContent = 'Error fetching data';
    }
  } catch (error) {
    responseDiv.textContent = 'Network error';
  }
});
&lt;/script&gt;</code></pre>
            <p>We've created a button. Upon clicking, Cloudflare makes a <code>fetch()</code> request to the <code>/your-api</code> endpoint, showing the result in the response container.</p><p>Now let’s consider that we have a Cloudflare WAF rule set up that protects the <code>/your-api</code> endpoint with a Managed Challenge.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1sjpmlJe4atSe3ztUjbL2M/99335880b870554a9c1dd3e5c8d70614/pasted-image-0-3.png" />
            
            </figure><p>Due to this rule, the app that we just wrote is going to fail for the reason described earlier (the browser is expecting a JSON response, but instead receives the challenge page as HTML).</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4HJrHiNoxjmSdRwEcZrYuA/a62895eaa382e160eb17fce51acde32c/Screenshot-2023-12-18-at-12.00.16.png" />
            
            </figure><p>If we inspect the Network Tab, we can see that the request to <code>/your-api</code> has been given a 403 response.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2DiC7Lus2CVxUJKw5pr7mi/ab45a3af70f411998ebb4892977a255d/image10.png" />
            
            </figure><p>Upon inspection, the Cf-Mitigated header shows that the response was challenged by Cloudflare’s firewall, as the visitor has not solved a challenge before.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2D81qpEEa60G1W1pZMUr2U/f04a2571ed6f52a16f6bf28adaee9ee4/image6.png" />
            
            </figure><p>To address this problem in our app, we set up a Turnstile Widget in Pre-Clearance mode for the Turnstile sitekey that we want to use.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6xLOm4TvNFK32gEf45I1XF/7e04c0a1fdc746a64dc8cf1e08ec3bf1/image2-4.png" />
            
            </figure><p>In our application, we override the <code>fetch()</code> function to invoke Turnstile once a Cf-Mitigated response has been received.</p>
            <pre><code>&lt;script&gt;
turnstileLoad = function () {
  // Save a reference to the original fetch function
  const originalFetch = window.fetch;

  // A simple modal to contain Cloudflare Turnstile
  const overlay = document.createElement('div');
  overlay.style.position = 'fixed';
  overlay.style.top = '0';
  overlay.style.left = '0';
  overlay.style.right = '0';
  overlay.style.bottom = '0';
  overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
  overlay.style.border = '1px solid grey';
  overlay.style.zIndex = '10000';
  overlay.style.display = 'none';
  overlay.innerHTML =       '&lt;p style="color: white; text-align: center; margin-top: 50vh;"&gt;One more step before you proceed...&lt;/p&gt;&lt;div style=”display: flex; flex-wrap: nowrap; align-items: center; justify-content: center;” id="turnstile_widget"&gt;&lt;/div&gt;';
  document.body.appendChild(overlay);

  // Override the native fetch function
  window.fetch = async function (...args) {
      let response = await originalFetch(...args);

      // If the original request was challenged...
      if (response.headers.has('cf-mitigated') &amp;&amp; response.headers.get('cf-mitigated') === 'challenge') {
          // The request has been challenged...
          overlay.style.display = 'block';

          await new Promise((resolve, reject) =&gt; {
              turnstile.render('#turnstile_widget', {
                  'sitekey': ‘YOUR_TURNSTILE_SITEKEY',
                  'error-callback': function (e) {
                      overlay.style.display = 'none';
                      reject(e);
                  },
                  'callback': function (token, preClearanceObtained) {
                      if (preClearanceObtained) {
                          // The visitor successfully solved the challenge on the page. 
                          overlay.style.display = 'none';
                          resolve();
                      } else {
                          reject(new Error('Unable to obtain pre-clearance'));
                      }
                  },
              });
          });

          // Replay the original fetch request, this time it will have the cf_clearance Cookie
          response = await originalFetch(...args);
      }
      return response;
  };
};
&lt;/script&gt;
&lt;script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=turnstileLoad" async defer&gt;&lt;/script&gt;</code></pre>
            <p>There is a lot going on in the snippet above: First, we create a hidden overlay element and override the browser’s <code>fetch()</code> function. The <code>fetch()</code> function is changed to introspect the Cf-Mitigated header for ‘challenge’. If a challenge is issued, the initial result will be unsuccessful; instead, a Turnstile overlay (with Pre-Clearance enabled) will appear in our web application. Once the Turnstile challenge has been completed we will retry the previous request after Turnstile has obtained the cf_clearance cookie to get through the Cloudflare WAF.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1HNSoEaOmTMmQFuc8kKY2p/1877b884856e092cfc51637f3f050c2c/image1-2.png" />
            
            </figure><p>Upon solving the Turnstile widget, the overlay disappears, and the requested API result is shown successfully:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7aPtaSfD7JdV0jYb1iDX50/dd9807c4807f6234dcb453471f43db99/Screenshot-2023-12-18-at-12.02.56.png" />
            
            </figure>
    <div>
      <h3>Pre-Clearance is available to all Cloudflare customers</h3>
      <a href="#pre-clearance-is-available-to-all-cloudflare-customers">
        
      </a>
    </div>
    <p>Every Cloudflare user with a <a href="https://www.cloudflare.com/plans/free/">free plan</a> or above can use Turnstile in managed mode free for an unlimited number of requests. If you’re a Cloudflare user looking to improve your security and user experience for your critical API endpoints, head over to our dashboard and <a href="https://dash.cloudflare.com/?to=/:account/turnstile">create a Turnstile widget with Pre-Clearance</a> today.</p> ]]></content:encoded>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[CAPTCHA]]></category>
            <category><![CDATA[Bots]]></category>
            <category><![CDATA[Turnstile]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Micro-frontends]]></category>
            <guid isPermaLink="false">1aYnXBUBD1B2KvKgz0veFW</guid>
            <dc:creator>Adam Martinetti</dc:creator>
            <dc:creator>Benedikt Wolters</dc:creator>
            <dc:creator>Miguel de Moura</dc:creator>
        </item>
        <item>
            <title><![CDATA[Incremental adoption of micro-frontends with Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/fragment-piercing/</link>
            <pubDate>Thu, 17 Nov 2022 14:00:00 GMT</pubDate>
            <description><![CDATA[ With Cloudflare Workers, our fragment-based micro-frontend architecture, and fragment piercing technique, engineering teams can incrementally improve large frontends in a fraction of the time, yielding significant user and developer experience gains. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Q4iw5pvW4frJry6aDHbW1/72ed68595be7d127566b976c0a4114a6/image5-11.png" />
            
            </figure>
    <div>
      <h2>Bring micro-frontend benefits to legacy Web applications</h2>
      <a href="#bring-micro-frontend-benefits-to-legacy-web-applications">
        
      </a>
    </div>
    <p>Recently, we wrote about <a href="/better-micro-frontends/">a new fragment architecture</a> for building Web applications that is fast, cost-effective, and scales to the largest projects, while enabling a fast iteration cycle. The approach uses multiple collaborating Cloudflare Workers to render and stream micro-frontends into an application that is interactive faster than traditional client-side approaches, leading to better user experience and SEO scores.</p><p>This approach is great if you are starting a new project or have the capacity to rewrite your current application from scratch. But in reality most projects are too large to be rebuilt from scratch and can adopt architectural changes only in an incremental way.</p><p>In this post we propose a way to replace only selected parts of a legacy client-side rendered application with server-side rendered fragments. The result is an application where the most important views are interactive sooner, can be developed independently, and receive all the benefits of the micro-frontend approach, while avoiding large rewrites of the legacy codebase. This approach is framework-agnostic; in this post we demonstrate fragments built with React, Qwik, and SolidJS.</p>
    <div>
      <h2>The pain of large frontend applications</h2>
      <a href="#the-pain-of-large-frontend-applications">
        
      </a>
    </div>
    <p>Many large frontend applications developed today fail to deliver good user experience. This is often caused by architectures that require large amounts of JavaScript to be downloaded, parsed and executed before users can interact with the application. Despite efforts to defer non-critical JavaScript code via lazy loading, and the use of server-side rendering, these large applications still take too long to become interactive and respond to the user's inputs.</p><p>Furthermore, large monolithic applications can be complex to build and deploy. Multiple teams may be collaborating on a single codebase and the effort to coordinate testing and deployment of the project makes it hard to develop, deploy and iterate on individual features.</p><p>As outlined in our <a href="/better-micro-frontends/">previous post</a>, micro-frontends powered by <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> can solve these problems but converting an application monolith to a micro-frontend architecture can be difficult and expensive. It can take months, or even years, of engineering time before any benefits are perceived by users or developers.</p><p>What we need is an approach where a project can incrementally adopt micro-frontends into the most impactful parts of the application incrementally, without needing to rewrite the whole application in one go.</p>
    <div>
      <h2>Fragments to the rescue</h2>
      <a href="#fragments-to-the-rescue">
        
      </a>
    </div>
    <p>The goal of a fragment based architecture is to significantly <a href="https://www.cloudflare.com/solutions/ecommerce/optimization/">decrease loading and interaction latency</a> for large web applications (as measured via <a href="https://web.dev/vitals/">Core Web Vitals</a>) by breaking the application into micro-frontends that can be quickly rendered (and cached) in Cloudflare Workers. The challenge is how to integrate a micro-frontend fragment into a legacy client-side rendered application with minimal cost to the original project.</p><p>The technique we propose allows us to convert the most valuable parts of a legacy application’s UI, in isolation from the rest of the application.</p><p>It turns out that, in many applications, the most valuable parts of the UI are often nested within an application “shell” that provides header, footer, and navigational elements. Examples of these include a login form, product details panel in an <a href="https://www.cloudflare.com/ecommerce/">e-commerce application</a>, the inbox in an email client, etc.</p><p>Let’s take a login form as an example. If it takes our application several seconds to display the login form, the users will dread logging in, and we might lose them. We can however convert the login form into a server-side rendered fragment, which is displayed and interactive immediately, while the rest of the legacy application boots up in the background. Since the fragment is interactive early, the user can even submit their credentials before the legacy application has started and rendered the rest of the page.</p><div></div>
<p><small>Animation showing the login form being available before the main application</small></p><p>This approach enables engineering teams to deliver valuable improvements to users in just a fraction of the time and engineering cost compared to traditional approaches, which either sacrifice user experience improvements, or require a lengthy and high-risk rewrite of the entire application. It allows teams with monolithic single-page applications to adopt a micro-frontend architecture incrementally, target the improvements to the most valuable parts of the application, and therefore front-load the return on investment.</p><p>An interesting challenge in extracting parts of the UI into server-side rendered fragments is that, once displayed in the browser, we want the legacy application and the fragments to feel like a single application. The fragments should be neatly embedded within the legacy application shell, keeping the application accessible by correctly forming the DOM hierarchy, but we also want the server-side rendered fragments to be displayed and become interactive as quickly as possible — even before the legacy client-side rendered application shell comes into existence. How can we embed UI fragments into an application shell that doesn’t exist yet? We resolved this problem via a technique we devised, which we call “fragment piercing”.</p>
    <div>
      <h2>Fragment piercing</h2>
      <a href="#fragment-piercing">
        
      </a>
    </div>
    <p>Fragment piercing combines HTML/DOM produced by server-side rendered micro-frontend fragments with HTML/DOM produced by a legacy client-side rendered application.</p><p>The micro-frontend fragments are rendered directly into the top level of the HTML response, and are designed to become immediately interactive. In the background, the legacy application is client-side rendered as a sibling of these fragments. When it is ready, the fragments are “pierced” into the legacy application – the DOM of each fragment is moved to its appropriate place within the DOM of the legacy application – without causing any visual side effects, or loss of client-side state, such as focus, form data, or text selection. Once “pierced”, a fragment can begin to communicate with the legacy application, effectively becoming an integrated part of it.</p><p>Here, you can see a “login” fragment and the empty legacy application “root” element at the top level of the DOM, before piercing.</p>
            <pre><code>&lt;body&gt;
  &lt;div id="root"&gt;&lt;/div&gt;
  &lt;piercing-fragment-host fragment-id="login"&gt;
    &lt;login q:container...&gt;...&lt;/login&gt;
  &lt;/piercing-fragment-host&gt;
&lt;/body&gt;</code></pre>
            <p>And here you can see that the fragment has been pierced into the “login-page” div in the rendered legacy application.</p>
            <pre><code>&lt;body&gt;
  &lt;div id="root"&gt;
    &lt;header&gt;...&lt;/header&gt;
    &lt;main&gt;
      &lt;div class="login-page"&gt;
        &lt;piercing-fragment-outlet fragment-id="login"&gt;
          &lt;piercing-fragment-host fragment-id="login"&gt;
            &lt;login  q:container...&gt;...&lt;/login&gt;
          &lt;/piercing-fragment-host&gt;
        &lt;/piercing-fragment-outlet&gt;
      &lt;/div&gt;
    &lt;/main&gt;
    &lt;footer&gt;...&lt;/footer&gt;
  &lt;/div&gt;
&lt;/body&gt;</code></pre>
            <p>To keep the fragment from moving and causing a visible layout shift during this transition, we apply CSS styles that position the fragment in the same way before and after piercing.</p><p>At any time an application can be displaying any number of pierced fragments, or none at all. This technique is not limited only to the initial load of the legacy application. Fragments can also be added to and removed from an application, at any time. This allows fragments to be rendered in response to user interactions and client-side routing.</p><p>With fragment piercing, you can start to incrementally adopt micro-frontends, one fragment at a time. You decide on the granularity of fragments, and which parts of the application to turn into fragments. The fragments don’t all have to use the same Web framework, which can be useful when switching stacks, or during a post-acquisition integration of multiple applications.</p>
    <div>
      <h2>The “Productivity Suite” demo</h2>
      <a href="#the-productivity-suite-demo">
        
      </a>
    </div>
    <p>As a demonstration of fragment piercing and incremental adoption we have developed a <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite">“productivity suite” demo</a> application that allows users to manage to-do lists, read hacker news, etc. We implemented the shell of this application as a client-side rendered React application — a common tech choice in corporate applications. This is our “legacy application”. There are three routes in the application that have been updated to use micro-frontend fragments:</p><ul><li><p><code>/login</code> - a simple dummy login form with client-side validation, displayed when users are not authenticated (implemented in <a href="https://qwik.builder.io/">Qwik</a>).</p></li><li><p><code>/todos</code> - manages one or more todo lists, implemented as two collaborating fragments:</p><ul><li><p>Todo list selector - a component for selecting/creating/deleting Todo lists (implemented in <a href="https://qwik.builder.io/">Qwik</a>).</p></li><li><p>Todo list editor - a clone of the <a href="https://todomvc.com/">TodoMVC</a> app (implemented in <a href="https://reactjs.org/docs/react-dom-server.html">React</a>).</p></li></ul></li><li><p><code>/news</code> - a clone of the <a href="https://github.com/solidjs/solid-hackernews">HackerNews</a> demo (implemented in <a href="https://www.solidjs.com/">SolidJS</a>).</p></li></ul><p>This demo showcases that different independent technologies can be used for both the legacy application and for each of the fragments.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/38jTYqRteZyGozPUoqXd8D/60b003aa2b53395b4adcb0cf31dcd2fc/image2-41.png" />
            
            </figure><p>A visualization of the fragments that are pierced into the legacy application</p><p>The application is deployed at <a href="https://productivity-suite.web-experiments.workers.dev/">https://productivity-suite.web-experiments.workers.dev/</a>.</p><p>To try it out, you first need to log in – simply use any username you like (no password needed). The user’s data is saved in a cookie, so you can log out and back in using the same username. After you’ve logged in, navigate through the various pages using the navigation bar at the top of the application. In particular, take a look at the “<a href="https://productivity-suite.web-experiments.workers.dev/todos">Todo Lists</a>” and “<a href="https://productivity-suite.web-experiments.workers.dev/news">News</a>” pages to see the piercing in action.</p><p>At any point, try reloading the page to see that fragments are rendered instantly while the legacy application loads slowly in the background. Try interacting with the fragments even before the legacy application has appeared!</p><p>At the very top of the page there are controls to let you see the impact of fragment piercing in action.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/11Y8DDksAEPz1AMQT8jjSG/8c69e3cc1d99b8a67a01410b146f2c02/image1-56.png" />
            
            </figure><ul><li><p>Use the “Legacy app bootstrap delay” slider to set the simulated delay before the legacy application starts.</p></li><li><p>Toggle “Piercing Enabled” to see what the user experience would be if the app did not use fragments.</p></li><li><p>Toggle “Show Seams” to see where each fragment is on the current page.</p></li></ul>
    <div>
      <h2>How it works</h2>
      <a href="#how-it-works">
        
      </a>
    </div>
    <p>The application is composed of a number of building blocks.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/47B8E1C6o3kWhsEzUWYL7J/ca1e6348128985d560bd28f0dc32615f/Frame-653.png" />
            
            </figure><p>An overview of the collaborating Workers and legacy application host</p><p>The <b>Legacy application host</b> in our demo serves the files that define the client-side React application (HTML, JavaScript and stylesheets). Applications built with other tech stacks would work just as well. The <b>Fragment Workers</b> host the micro-frontend fragments, as described in our previous <a href="/better-micro-frontends/">fragment architecture</a> post. And the <b>Gateway Worker</b> handles requests from the browser, selecting, fetching and combining response streams from the legacy application and micro-frontend fragments.</p><p>Once these pieces are all deployed, they work together to handle each request from the browser. Let’s look at what happens when you go to the `/login` route.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2atnhkVHu0tNa1MKGWnOPs/b80838e713fd7b64a177a82b06377a16/image4-22.png" />
            
            </figure><p>The flow of requests when viewing the login page</p><p>The user navigates to the application and the browser makes a request to the Gateway Worker to get the initial HTML. The Gateway Worker identifies that the browser is requesting the login page. It then makes two parallel sub-requests – one to fetch the index.html of the legacy application, and another to request the server-side rendered login fragment. It then combines these two responses into a single response stream containing the HTML that is delivered to the browser.</p><p>The browser displays the HTML response containing the empty root element for the legacy application, and the server-side rendered login fragment, which is immediately interactive for the user.</p><p>The browser then requests the legacy application’s JavaScript. This request is proxied by the Gateway Worker to the Legacy application host. Similarly, any other assets for the legacy application or fragments get routed through the Gateway Worker to the legacy application host or appropriate Fragment Worker.</p><p>Once the legacy application’s JavaScript has been downloaded and executed, rendering the shell of the application in the process, the fragment piercing kicks in, moving the fragment into the appropriate place in the legacy application, while preserving all of its UI state.</p><p>While focussed on the login fragment to explain fragment piercing, the same ideas apply to the other fragments implemented in the <code>/todos</code> and <code>/news</code> routes.</p>
    <div>
      <h2>The piercing library</h2>
      <a href="#the-piercing-library">
        
      </a>
    </div>
    <p>Despite being implemented using different Web frameworks, all the fragments are integrated into the legacy application in the same way using helpers from a “<a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite/piercing-library">Piercing Library</a>”. This library is a collection of server-side and client-side utilities that we developed, for the demo, to handle integrating the legacy application with micro-frontend fragments. The main features of the library are the <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-gateway.ts#L82"><code>PiercingGateway</code></a> class, <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-host/piercing-fragment-host.ts#L5">fragment host</a> and <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-outlet.ts#L31">fragment outlet</a> custom elements, and the <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/message-bus/message-bus.ts#L18"><code>MessageBus</code></a> class.</p>
    <div>
      <h3>PiercingGateway</h3>
      <a href="#piercinggateway">
        
      </a>
    </div>
    <p>The <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-gateway.ts#L82"><code>PiercingGateway</code></a> class can be used to instantiate a Gateway Worker that handles all requests for our application’s HTML, JavaScript and other assets. The `PiercingGateway` routes requests through to the appropriate Fragment Workers or to the host of the Legacy Application. It also combines the HTML response streams from these fragments with the response from the legacy application into a single HTML stream that is returned to the browser.</p><p>Implementing a Gateway Worker is straightforward using the Piercing Library. Create a new <code>gateway</code> instance of <code>PiercingGateway</code>, passing it the URL to the legacy application host and a function to determine whether piercing is enabled for the given request. Export the <code>gateway</code> as the default export from the Worker script so that the Workers runtime can wire up its <code>fetch()</code> handler.</p>
            <pre><code>const gateway = new PiercingGateway&lt;Env&gt;({
  // Configure the origin URL for the legacy application.
  getLegacyAppBaseUrl: (env) =&gt; env.APP_BASE_URL,
  shouldPiercingBeEnabled: (request) =&gt; ...,
});
...

export default gateway;</code></pre>
            <p>Fragments can be registered by calling the <code>registerFragment()</code> method so that the <code>gateway</code> can automatically route requests for a fragment’s HTML and assets to its Fragment Worker. For example, registering the login fragment would look like:</p>
            <pre><code>gateway.registerFragment({
  fragmentId: "login",
  prePiercingStyles: "...",
  shouldBeIncluded: async (request) =&gt; !(await isUserAuthenticated(request)),
});</code></pre>
            
    <div>
      <h3>Fragment host and outlet</h3>
      <a href="#fragment-host-and-outlet">
        
      </a>
    </div>
    <p>Routing requests and combining HTML responses in the Gateway Worker is only half of what makes piercing possible. The other half needs to happen in the browser where the fragments need to be pierced into the legacy application using the technique we described earlier.</p><p>The fragment piercing in the browser is facilitated by a pair of <a href="https://html.spec.whatwg.org/multipage/custom-elements.html">custom elements</a>, the fragment host (<a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-host/piercing-fragment-host.ts#L5"><code>&lt;piercing-fragment-host&gt;</code></a>) and the fragment outlet (<a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-outlet.ts#L31"><code>&lt;piercing-fragment-outlet&gt;</code></a>).</p><p>The Gateway Worker wraps the HTML for each fragment in a fragment host. In the browser, the fragment host manages the life-time of the fragment and is used when moving the fragment’s DOM into position in the legacy application.</p>
            <pre><code>&lt;piercing-fragment-host fragment-id="login"&gt;
  &lt;login q:container...&gt;...&lt;/login&gt;
&lt;/piercing-fragment-host&gt;</code></pre>
            <p>In the legacy application, the developer marks where a fragment should appear when it is pierced by adding a fragment outlet. Our demo application’s Login route looks as follows:</p>
            <pre><code>export function Login() {
  …
  return (
    &lt;div className="login-page" ref={ref}&gt;
      &lt;piercing-fragment-outlet fragment-id="login" /&gt;
    &lt;/div&gt;
  );
}</code></pre>
            <p>When a fragment outlet is added to the DOM, it searches the current document for its associated fragment host. If found, the fragment host and its contents are moved inside the outlet. If the fragment host is not found, the outlet will make a request to the gateway worker to fetch the fragment HTML, which is then streamed directly into the fragment outlet, using the <a href="https://github.com/marko-js/writable-dom">writable-dom library</a> (a small but powerful library developed by the <a href="https://markojs.com/">MarkoJS</a> team).</p><p>This fallback mechanism enables client-side navigation to routes that contain new fragments. This way fragments can be rendered in the browser via both initial (hard) navigation and client-side (soft) navigation.</p>
    <div>
      <h3>Message bus</h3>
      <a href="#message-bus">
        
      </a>
    </div>
    <p>Unless the fragments in our application are completely presentational or self-contained, they also need to communicate with the legacy application and other fragments. The <code>[MessageBus](https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/message-bus/message-bus.ts#L18)</code> is a simple asynchronous, isomorphic, and framework-agnostic communication bus that the legacy application and each of the fragments can access.</p><p>In our demo application the login fragment needs to inform the legacy application when the user has authenticated. This <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/app/fragments/login/src/components/LoginForm.tsx#L51-L57">message dispatch</a> is implemented in the Qwik <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/app/fragments/login/src/components/LoginForm.tsx#L38"><code>LoginForm</code></a> component as follows:</p>
            <pre><code>const dispatchLoginEvent = $(() =&gt; {
  getBus(ref.value).dispatch("login", {
    username: state.username,
    password: state.password,
  });
  state.loading = true;
});</code></pre>
            <p>The legacy application can then <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/app/legacy-app/src/auth.tsx#L24-L34">listen for these messages</a> like this:</p>
            <pre><code>useEffect(() =&gt; {
  return getBus().listen&lt;LoginMessage&gt;("login", async (user) =&gt; {
    setUser(user);
    await addUserDataIfMissing(user.username);
    await saveCurrentUser(user.username);
    getBus().dispatch("authentication", user);
    navigate("/", { replace: true, });
  });
}, []);</code></pre>
            <p>We settled on this message bus implementation because we needed a solution that was framework-agnostic, and worked well on both the server as well as client.</p>
    <div>
      <h2>Give it a go!</h2>
      <a href="#give-it-a-go">
        
      </a>
    </div>
    <p>With fragments, fragment piercing, and Cloudflare Workers, you can improve performance as well as the development cycle of legacy client-side rendered applications. These changes can be adopted incrementally, and you can even do so while implementing fragments with a Web framework for your choice.</p><p>The “Productivity Suite” application demonstrating these capabilities can be found at <a href="https://productivity-suite.web-experiments.workers.dev/">https://productivity-suite.web-experiments.workers.dev/</a>.</p><p>All the code we have shown is open-source and published to Github: <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite">https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite</a>.</p><p>Feel free to clone the repository. It is easy to run locally and even deploy your own version (for free) to Cloudflare. We tried to make the code as reusable as possible. Most of the core logic is in the <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite/piercing-library">piercing library</a> that you could try in your own projects. We’d be thrilled to receive feedback, suggestions, or hear about applications you’d like to use it for. Join our <a href="https://github.com/cloudflare/workers-web-experiments/discussions/64">GitHub discussion</a> or also reach us on our <a href="https://discord.com/channels/595317990191398933/1041751020340002907">discord channel</a>.</p><p>We believe that combining Cloudflare Workers with the latest ideas from frameworks will drive the next big steps forward in improved experiences for both users and developers in Web applications. Expect to see more demos, blog posts and collaborations as we continue to push the boundaries of what the Web can offer. And if you’d also like to be directly part of this journey, we are also happy to share that <a href="https://boards.greenhouse.io/cloudflare/jobs/4619341">we are hiring</a>!</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Micro-frontends]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">4vnFkyih2W2DD0QcaALcdf</guid>
            <dc:creator>Peter Bacon Darwin</dc:creator>
            <dc:creator>Dario Piotrowicz</dc:creator>
            <dc:creator>James Culveyhouse</dc:creator>
            <dc:creator>Igor Minar</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare Workers and micro-frontends: made for one another]]></title>
            <link>https://blog.cloudflare.com/better-micro-frontends/</link>
            <pubDate>Thu, 20 Oct 2022 13:00:00 GMT</pubDate>
            <description><![CDATA[ In this blog-post we demonstrate how hosting and combining multiple server-side rendered micro-frontends on Cloudflare Workers offer a highly scalable, high performance solution to these problems ]]></description>
            <content:encoded><![CDATA[ <p>To help developers build better web applications we researched and devised a fragments architecture to build <a href="https://martinfowler.com/articles/micro-frontends.html">micro-frontends</a> using <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> that is lightning fast, cost-effective to develop and operate, and scales to the needs of the largest enterprise teams without compromising release velocity or user experience.</p><p>Here we share a technical overview and a proof of concept of this architecture.</p>
    <div>
      <h2>Why micro-frontends?</h2>
      <a href="#why-micro-frontends">
        
      </a>
    </div>
    <p>One of the challenges of modern frontend web development is that applications are getting bigger and more complex. This is especially true for enterprise web applications supporting <a href="https://www.cloudflare.com/ecommerce/">e-commerce</a>, banking, insurance, travel, and other industries, where a unified user interface provides access to a large amount of functionality. In such projects it is common for many teams to collaborate to build a single web application. These monolithic web applications, usually built with JavaScript technologies like React, Angular, or Vue, span thousands, or even millions of lines of code.</p><p>When a monolithic JavaScript architecture is used with applications of this scale, the result is a slow and fragile user experience with low <a href="https://web.dev/measure/">Lighthouse scores</a>. Furthermore, collaborating development teams often struggle to <a href="https://www.youtube.com/watch?v=pU1gXA0rfwc">maintain and evolve</a> their parts of the application, as their <a href="https://igor.dev/posts/fate-sharing-and-micro-frontends/">fates are tied</a> with fates of all the other teams, so the mistakes and tech debt of one team often impacts all.</p><p>Drawing on ideas from <a href="https://en.wikipedia.org/wiki/Microservices">microservices</a>, the frontend community has started to advocate for <a href="https://micro-frontends.org/">micro-frontends</a> to enable teams to develop and deploy their features independently of other teams. Each micro-frontend is a self-contained mini-application, that can be developed and released independently, and is responsible for rendering a “fragment” of the page. The application then combines these fragments together so that from the user's perspective it feels like a single application.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/78VTFfttaimJC7Son9VcYZ/652e6f47dd6354864fd99102307659ba/image3.jpg" />
            
            </figure><p>An application consisting of multiple micro-frontends</p><p>Fragments could represent vertical application features, like “account management” or “checkout”, or horizontal features, like “header” or “navigation bar”.</p>
    <div>
      <h3>Client-side micro-frontends</h3>
      <a href="#client-side-micro-frontends">
        
      </a>
    </div>
    <p>A common approach to micro-frontends is to rely upon client-side code to lazy load and stitch fragments together (e.g. via <a href="https://webpack.js.org/concepts/module-federation/">Module Federation</a>). Client-side micro-frontend applications suffer from a number of problems.</p><p>Common code must either be duplicated or published as a shared library. Shared libraries are problematic themselves. It is not possible to <a href="https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking">tree-shake</a> unused library code at build time resulting in more code than necessary being downloaded to the browser and coordinating between teams when shared libraries need to be updated can be complex and awkward.</p><p>Also, the top-level container application must bootstrap before the micro-frontends can even be requested, and they also need to boot before they become interactive. If they are nested, then you may end up getting a <a href="https://javascript.plainenglish.io/react-official-answer-the-right-way-of-requesting-data-in-react18-other-frameworks-also-apply-50d907c1f6c4">waterfall of requests</a> to get micro-frontends leading to further runtime delays.</p><p>These problems can result in a sluggish application startup experience for the user.</p><p>Server-side rendering could be used with client-side micro-frontends to improve how quickly a browser displays the application but implementing this can significantly increase the complexity of development, deployment and operation. Furthermore, most server-side rendering approaches still suffer from a <a href="https://dev.to/this-is-learning/why-efficient-hydration-in-javascript-frameworks-is-so-challenging-1ca3">hydration delay</a> before the user can fully interact with the application.</p><p>Addressing these challenges was the main motivation for exploring an alternative solution, which relies on the distributed, low latency properties provided by Cloudflare Workers.</p>
    <div>
      <h2>Micro-frontends on Cloudflare Workers</h2>
      <a href="#micro-frontends-on-cloudflare-workers">
        
      </a>
    </div>
    <p><a href="https://workers.cloudflare.com/">Cloudflare Workers</a> is a compute platform that offers a highly scalable, low latency JavaScript execution environment that is available in <a href="https://www.cloudflare.com/network/">over 275 locations</a> around the globe. In our exploration we used Cloudflare Workers to host and render micro-frontends from anywhere on our global network.</p>
    <div>
      <h3>Fragments architecture</h3>
      <a href="#fragments-architecture">
        
      </a>
    </div>
    <p>In this architecture the application consists of a tree of “fragments” each deployed to Cloudflare Workers that collaborate to server-side render the overall response. The browser makes a request to a “root fragment”, which will communicate with “child fragments” to generate the final response. Since Cloudflare Workers can communicate with each other <a href="https://developers.cloudflare.com/workers/platform/bindings/about-service-bindings/">with almost no overhead</a>, applications can be server-side rendered quickly by child fragments, all working in parallel to render their own HTML, streaming their results to the parent fragment, which combines them into the final response stream delivered to the browser.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/43AlwyalVCD1CfOm31lNiX/f740b01c554b0f574267cf8c4dfe245c/blog-1447.png" />
            
            </figure><p>A high-level overview of a fragments architecture</p>
    <div>
      <h2>Visit the “Cloud Gallery”</h2>
      <a href="#visit-the-cloud-gallery">
        
      </a>
    </div>
    <p>We have built an example of a “Cloud Gallery” application to show how this can work in practice. It is deployed to Cloudflare Workers at  <a href="https://cloud-gallery.web-experiments.workers.dev/">https://cloud-gallery.web-experiments.workers.dev/</a></p><p>The demo application is a simple filtered gallery of cloud images built using our fragments architecture. Try selecting a tag in the type-ahead to filter the images listed in the gallery. Then change the delay on the stream of cloud images to see how the type-ahead filtering can be interactive before the page finishes loading.</p>
    <div>
      <h3>Multiple Cloudflare Workers</h3>
      <a href="#multiple-cloudflare-workers">
        
      </a>
    </div>
    <p>The application is composed of a tree of six collaborating but independently deployable Cloudflare Workers, each rendering their own fragment of the screen and providing their own client-side logic, and assets such as CSS stylesheets and images.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2xfKbBmdC16pAkNbdCjOz7/0a3e7eebadd5272421fe10dbbb594d76/image6-4.png" />
            
            </figure><p>Architectural overview of the Cloud Gallery app</p><p>The “main” fragment acts as the root of the application. The “header” fragment has a slider to configure an artificial delay to the display of gallery images. The “body” fragment contains the “filter” fragment and “gallery” fragments. Finally, the “footer” fragment just shows some static content.</p><p>The full source code of the demo app is available on <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/cloud-gallery">GitHub</a>.</p>
    <div>
      <h2>Benefits and features</h2>
      <a href="#benefits-and-features">
        
      </a>
    </div>
    <p>This architecture of multiple collaborating server-side rendered fragments, deployed to Cloudflare Workers has some interesting features.</p>
    <div>
      <h3>Encapsulation</h3>
      <a href="#encapsulation">
        
      </a>
    </div>
    <p>Fragments are entirely encapsulated, so they can control what they own and what they make available to other fragments.</p>
    <div>
      <h4><i>Fragments can be developed and deployed independently</i></h4>
      <a href="#fragments-can-be-developed-and-deployed-independently">
        
      </a>
    </div>
    <p>Updating one of the fragments is as simple as redeploying that fragment. The next request to the main application will use the new fragment. Also, fragments can host their own assets (client-side JavaScript, images, etc.), which are streamed through their parent fragment to the browser.</p>
    <div>
      <h4><i>Server-only code is not sent to the browser</i></h4>
      <a href="#server-only-code-is-not-sent-to-the-browser">
        
      </a>
    </div>
    <p>As well as reducing the cost of downloading unnecessary code to the browser, security sensitive code that is only needed for server-side rendering the fragment is never exposed to other fragments and is not downloaded to the browser. Also, features can be safely hidden behind feature flags in a fragment, allowing more flexibility with rolling out new behavior safely.</p>
    <div>
      <h3>Composability</h3>
      <a href="#composability">
        
      </a>
    </div>
    <p>Fragments are fully composable - any fragment can contain other fragments. The resulting tree structure gives you more flexibility in how you architect and deploy your application. This helps larger projects to scale their development and deployment. Also, fine-grain control over how fragments are composed, could allow fragments that are expensive to server-side render to be cached individually.</p>
    <div>
      <h3>Fantastic Lighthouse scores</h3>
      <a href="#fantastic-lighthouse-scores">
        
      </a>
    </div>
    <p>Streaming server-rendered HTML results in great user experiences and <a href="https://developer.chrome.com/docs/lighthouse/overview/">Lighthouse</a> scores, which in practice means happier users and higher chance of conversions for your business.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4GNNIIhE13dbX7EwfTUoQ8/47168493eec000d1df7eb71db56bf271/image7-2.png" />
            
            </figure><p>Lighthouse scores for the Cloud Gallery app</p><p>Each fragment can parallelize requests to its child fragments and pipe the resulting HTML streams into its own single streamed server-side rendered response. Not only can this reduce the time to render the whole page but streaming each fragment through to the browser reduces the time to the first byte of each fragment.</p>
    <div>
      <h3>Eager interactivity</h3>
      <a href="#eager-interactivity">
        
      </a>
    </div>
    <p>One of the powers of a fragments architecture is that fragments can become interactive even while the rest of the application (including other fragments) is still being streamed down to the browser.</p><p>In our demo, the “filter” fragment is immediately interactive as soon as it is rendered, even if the image HTML for the “gallery” fragment is still loading.</p><p>To make it easier to see this, we added a slider to the top of the “header” that can simulate a network or database delay that slows down the HTML stream which renders the “gallery” images. Even when the “gallery” fragment is still loading, the type-ahead input, in the “filter” fragment, is already fully interactive.</p><p>Just think of all the frustration that this eager interactivity could avoid for web application users with unreliable Internet connection.</p>
    <div>
      <h2>Under the hood</h2>
      <a href="#under-the-hood">
        
      </a>
    </div>
    <p>As discussed already this architecture relies upon deploying this application as many cooperating Cloudflare Workers. Let’s look into some details of how this works in practice.</p><p>We experimented with various technologies, and while this approach can be used with many frontend libraries and frameworks, we found the <a href="https://qwik.builder.io/">Qwik framework</a> to be a particularly good fit, because of its HTML-first focus and low JavaScript overhead, which avoids any hydration problems.</p>
    <div>
      <h3>Implementing a fragment</h3>
      <a href="#implementing-a-fragment">
        
      </a>
    </div>
    <p>Each fragment is a server-side rendered Qwik application deployed to its own Cloudflare Worker. This means that you can even browse to these fragments directly. For example, the “header” fragment is deployed to <a href="https://cloud-gallery-header.web-experiments.workers.dev/">https://cloud-gallery-header.web-experiments.workers.dev/</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/24SSUszIf2fbUphL5lsfr9/756d04fc6bc455f3845b45bf1ff592c0/image8-2.png" />
            
            </figure><p>A screenshot of the self-hosted “header” fragment</p><p>The header fragment is defined as a <a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/header/src/root.tsx"><code>Header</code></a> component using Qwik. This component is rendered in a Cloudflare Worker via a <code>fetch()</code> handler:</p>
            <pre><code>export default {
  fetch(request: Request, env: Record&lt;string, unknown&gt;): Promise&lt;Response&gt; {
    return renderResponse(request, env, &lt;Header /&gt;, manifest, "header");
  },
};</code></pre>
            <small><a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/header/src/entry.ssr.tsx"><b>cloud-gallery/header/src/entry.ssr.tsx</b></a></small><br /><p>The <a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/helpers/src/renderResponse.ts"><code><u>renderResponse</u></code></a><code>()</code> function is a helper we wrote that server-side renders the fragment and streams it into the body of a <code>Response</code> that we return from the <code>fetch()</code> handler.</p><p>The header fragment serves its own JavaScript and image assets from its Cloudflare Worker. We configure <a href="https://developers.cloudflare.com/workers/wrangler"><u>Wrangler</u></a> to upload these assets to Cloudflare and serve them from our network.</p>
    <div>
      <h2>Implementing fragment composition</h2>
      <a href="#implementing-fragment-composition">
        
      </a>
    </div>
    <p>Fragments that contain child fragments have additional responsibilities:</p><ul><li><p>Request and inject child fragments when rendering their own HTML.</p></li><li><p>Proxy requests for child fragment assets through to the appropriate fragment.</p></li></ul>
    <div>
      <h3>Injecting child fragments</h3>
      <a href="#injecting-child-fragments">
        
      </a>
    </div>
    <p>The position of a child fragment inside its parent can be specified by a <a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/helpers/src/fragmentHelpers.tsx"><code>FragmentPlaceholder</code></a> helper component that we have developed. For example, the “body” fragment has the “filter” and “gallery” fragments.</p>
            <pre><code>&lt;div class="content"&gt;
  &lt;FragmentPlaceholder name="filter" /&gt;
  &lt;FragmentPlaceholder name="gallery" /&gt;
&lt;/div&gt;</code></pre>
            <small><a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/body/src/root.tsx"><b>cloud-gallery/body/src/root.tsx</b></a></small><br /><p>The <code>FragmentPlaceholder</code> component is responsible for making a request for the fragment and piping the fragment stream into the output stream.</p>
    <div>
      <h3>Proxying asset requests</h3>
      <a href="#proxying-asset-requests">
        
      </a>
    </div>
    <p>As mentioned earlier, fragments can host their own assets, especially client-side JavaScript files. When a request for an asset arrives at the parent fragment, it needs to know which child fragment should receive the request.</p><p>In our demo we use a convention that such asset paths will be prefixed with <code>/_fragment/&lt;fragment-name&gt;</code>. For example, the header logo image path is <code>/_fragment/header/cf-logo.png</code>. We developed a <a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/helpers/src/fragmentHelpers.tsx#L28"><code><u>tryGetFragmentAsset</u></code></a><code>()</code> helper which can be added to the parent fragment’s <code>fetch()</code> handler to deal with this:</p>
            <pre><code>export default {
  async fetch(
    request: Request,
    env: Record&lt;string, unknown&gt;
  ): Promise&lt;Response&gt; {
    // Proxy requests for assets hosted by a fragment.
    const asset = await tryGetFragmentAsset(env, request);
    if (asset !== null) {
      return asset;
    }
    // Otherwise server-side render the template injecting child fragments.
    return renderResponse(request, env, &lt;Root /&gt;, manifest, "div");
  },
};</code></pre>
            <small><a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/body/src/entry.ssr.tsx"><strong>cloud-gallery/body/src/entry.ssr.tsx</strong></a></small><br />
    <div>
      <h3>Fragment asset paths</h3>
      <a href="#fragment-asset-paths">
        
      </a>
    </div>
    <p>If a fragment hosts its own assets, then we need to ensure that any HTML it renders uses the special <code>_fragment/&lt;fragment-name&gt;</code> path prefix mentioned above when referring to these assets. We have implemented a strategy for this in the helpers we developed.</p><p>The <a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/helpers/src/fragmentHelpers.tsx"><code><u>FragmentPlaceholder</u></code></a> component adds a `base` searchParam to the fragment request to tell it what this prefix should be. The <a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/helpers/src/renderResponse.ts"><code><u>renderResponse</u></code></a><code>()</code> helper extracts this prefix and provides it to the Qwik server-side renderer. This ensures that any request for client-side JavaScript has the correct prefix. Fragments can apply a hook that we developed called <a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/helpers/src/base.ts#L22"><code><u>useFragmentRoot</u></code></a><code>()</code>. This allows components to gather the prefix from a <a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/helpers/src/base.ts#L12"><code><u>FragmentContext</u></code></a> <a href="https://qwik.builder.io/docs/components/context/"><u>context</u></a>.</p><p>For example, since the “header” fragment hosts the Cloudflare and Github logos as assets, it must call the <code>useFragmentRoot()</code> hook:</p>
            <pre><code>export const Header = component$(() =&gt; {
  useStylesScoped$(HeaderCSS);
  useFragmentRoot();

  return (...);
});</code></pre>
            <small><a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/header/src/root.tsx"><strong>cloud-gallery/header/src/root.tsx</strong></a></small><p>The <code>FragmentContext</code> value can then be accessed in components that need to apply the prefix. For example, the <code>Image</code> component:</p>
            <pre><code>export const Image = component$((props: Record&lt;string, string | number&gt;) =&gt; {
  const { base } = useContext(FragmentContext);
  return &lt;img {...props} src={base + props.src} /&gt;;
});</code></pre>
            <small><a href="https://github.com/cloudflare/workers-web-experiments/blob/main/cloud-gallery/helpers/src/image/image.tsx"><strong>cloud-gallery/helpers/src/image/image.tsx</strong></a></small>
    <div>
      <h3>Service-binding fragments</h3>
      <a href="#service-binding-fragments">
        
      </a>
    </div>
    <p>Cloudflare Workers provide a mechanism called <a href="https://developers.cloudflare.com/workers/platform/bindings/about-service-bindings/">service bindings</a> to make requests between Cloudflare Workers efficiently that avoids network requests. In the demo we use this mechanism to make the requests from parent fragments to their child fragments with almost no performance overhead, while still allowing the fragments to be independently deployed.</p>
    <div>
      <h2>Comparison to current solutions</h2>
      <a href="#comparison-to-current-solutions">
        
      </a>
    </div>
    <p>This fragments architecture has three properties that distinguish it from other current solutions.</p><p>Unlike monoliths, or client-side micro-frontends, fragments are developed and deployed as independent server-side rendered applications that are composed together on the server-side. This significantly improves rendering speed, and lowers interaction latency in the browser.</p><p>Unlike server-side rendered micro-frontends with Node.js or cloud functions, Cloudflare Workers is a globally distributed compute platform with a region-less deployment model. It has incredibly low latency, and a near-zero communication overhead between fragments.</p><p>Unlike solutions based on <a href="https://webpack.js.org/concepts/module-federation/">module federation</a>, a fragment's client-side JavaScript is very specific to the fragment it is supporting. This means that it is small enough that we don’t need to have shared library code, eliminating the version skew issues and coordination problems when updating shared libraries.</p>
    <div>
      <h2>Future possibilities</h2>
      <a href="#future-possibilities">
        
      </a>
    </div>
    <p>This demo is just a proof of concept, so there are still areas to investigate. Here are some of the features we’d like to explore in the future.</p>
    <div>
      <h3>Caching</h3>
      <a href="#caching">
        
      </a>
    </div>
    <p>Each micro-frontend fragment can be cached independently of the others based on how static its content is. When requesting the full page, the fragments only need to run server-side rendering for micro-frontends that have changed.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3PxpkF17fG0fMqBX0nhT6n/1789385a464d8908f7646a1570c6314d/image2.jpg" />
            
            </figure><p>An application where the output of some fragments are cached</p><p>With per-fragment caching you can return the HTML response to the browser faster, and avoid incurring compute costs in re-rendering content unnecessarily.</p>
    <div>
      <h3>Fragment routing and client-side navigation</h3>
      <a href="#fragment-routing-and-client-side-navigation">
        
      </a>
    </div>
    <p>Our demo application used micro-frontend fragments to compose a single page. We could however use this approach to implement page routing as well. When server-side rendering, the main fragment could insert the appropriate “page” fragment based on the visited URL. When navigating, client-side, within the app, the main fragment would remain the same while the displayed “page” fragment would change.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/31uGNgOqJccEZXHRfrK7Fw/a69dd9152b184802d07c437347560251/image5-1.jpg" />
            
            </figure><p>An application where each route is delegated to a different fragment</p><p>This approach combines the best of server-side and client-side routing with the power of fragments.</p>
    <div>
      <h3>Using other frontend frameworks</h3>
      <a href="#using-other-frontend-frameworks">
        
      </a>
    </div>
    <p>Although the Cloud Gallery application uses Qwik to implement all fragments, it is possible to use other frameworks as well. If really necessary, it’s even possible to mix and match frameworks.</p><p>To achieve good results, the framework of choice should be capable of server-side rendering, and should have a small client-side JavaScript footprint. HTML streaming capabilities, while not required, can significantly improve performance of large applications.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1AkgbhioqHBaKtvZxVTIsp/6a818eeb06c4ad842886e81f9d0f477a/image1-15.png" />
            
            </figure><p>An application using different frontend frameworks</p>
    <div>
      <h3>Incremental migration strategies</h3>
      <a href="#incremental-migration-strategies">
        
      </a>
    </div>
    <p>Adopting a new architecture, compute platform, and deployment model is a lot to take in all at once, and for existing large applications is prohibitively risky and expensive. To make this  fragment-based architecture available to legacy projects, an incremental adoption strategy is a key.</p><p>Developers could test the waters by migrating just a single piece of the user-interface within their legacy application to a fragment, integrating with minimal changes to the legacy application. Over time, more of the application could then be moved over, one fragment at a time.</p>
    <div>
      <h3>Convention over configuration</h3>
      <a href="#convention-over-configuration">
        
      </a>
    </div>
    <p>As you can see in the Cloud Gallery demo application, setting up a fragment-based micro-frontend requires quite a bit of configuration. A lot of this configuration is very mechanical and could be abstracted away via conventions and better tooling. Following productivity-focused precedence found in Ruby on Rails, and filesystem based routing meta-frameworks, we could make a lot of this configuration disappear.</p>
    <div>
      <h2>Try it yourself!</h2>
      <a href="#try-it-yourself">
        
      </a>
    </div>
    <p>There is still so much to dig into! Web applications have come a long way in recent years and their growth is hard to overstate. Traditional implementations of micro-frontends have had only mixed success in helping developers scale development and deployment of large applications. Cloudflare Workers, however, unlock new possibilities which can help us tackle many of the existing challenges and help us build better web applications.</p><p>Thanks to the <a href="https://workers.cloudflare.com/#plans">generous free plan</a> offered by Cloudflare Workers, you can <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/cloud-gallery">check out the Gallery Demo code</a> and deploy it yourself.</p><p>If all of these sounds interesting to you, and you would like to work with us on improving the developer experience for Cloudflare Workers, we are also happy to share that <a href="https://boards.greenhouse.io/cloudflare/jobs/4619341">we are hiring</a>!</p> ]]></content:encoded>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Micro-frontends]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Serverless]]></category>
            <guid isPermaLink="false">irbcct7Wl9QqgfZhsBGew</guid>
            <dc:creator>Peter Bacon Darwin</dc:creator>
            <dc:creator>James Culveyhouse</dc:creator>
            <dc:creator>Igor Minar</dc:creator>
        </item>
    </channel>
</rss>