Cloudflare has introduced a new Worker template for Vertical Microfrontends, enabling development teams to map multiple independent Cloudflare Workers to a single domain. This architectural pattern allows teams to work in complete isolation—shipping marketing sites, documentation, and dashboards independently—while delivering a single, seamless application experience to users.

Beyond Horizontal Microfrontends

Most microfrontend architectures are "horizontal," meaning different parts of a single page are fetched from different services. Vertical microfrontends take a fundamentally different approach by splitting applications by URL path rather than page components.

In this model, a team owning the /blog path doesn't just own a component—they own the entire vertical stack for that route, including framework choice, libraries, CI/CD pipeline, and deployment process. This complete ownership allows teams to have true autonomy and ship with confidence, knowing their changes won't impact other teams' work.

The Growing Pains Problem

As organizations scale, they face increasingly complex technical challenges. Different frameworks serve different use cases—a marketing website might be better served by Astro, while a dashboard application might benefit from React. Teams working in monolithic codebases often face frustrating rollbacks when a single team's regression impacts an entire release containing updates from multiple teams.

How can we provide users with a cohesive experience while giving development teams full autonomy and control over their domains? Vertical microfrontends offer a compelling answer.

Defining Vertical Slices

A vertical microfrontend is an architectural pattern where a single independent team owns an entire slice of application functionality, from user interface down to the CI/CD pipeline. These slices are defined by URL paths, with individual Workers associated with specific paths:

/ = Marketing
/docs = Documentation
/blog = Blog
/dash = Dashboard

This approach extends to more granular sub-paths as well. Within a dashboard, features or products are typically segmented by URL path depth, such as /dash/product-a. Navigating between products could mean traversing entirely different codebases.

With vertical microfrontends, we can define:

/dash/product-a = WorkerA
/dash/product-b = WorkerB

Each path represents its own frontend project with zero shared code. The product-a and product-b routes map to separately deployed frontend applications with their own frameworks, libraries, CI/CD pipelines, defined and owned by distinct teams.

Finally, teams can own their code end-to-end. But this creates a new challenge: stitching separate projects together to create a unified user experience.

Creating Visually Unified Experiences

Making independent projects feel like a cohesive experience isn't as difficult as it might seem—it requires just a few lines of CSS magic. The absolute imperative is avoiding leaking implementation details and internal decisions to users. If the experience doesn't feel like one cohesive frontend, we've failed our users.

View Transitions

When seamlessly navigating between distinct pages while maintaining smooth user experience, view transitions are invaluable. Defining specific DOM elements to persist until the next page is visible, and specifying how changes are handled, provides powerful tools for multi-page applications.

There may be instances where making various vertical microfrontends feel different is acceptable. Marketing websites, documentation, and dashboards are often uniquely defined, and users wouldn't expect cohesion navigating between them. However, when introducing vertical slices within a specific experience like a dashboard, users should never know they're interacting with different repositories, workers, or projects underneath.

CSS View Transitions enable animated transitions between different views—whether single-page or multi-page applications—making them feel unified. Before adding view transitions, navigating between pages owned by different Workers would display an interstitial white blank screen for milliseconds until the next page began rendering. Pages wouldn't feel cohesive.

If we want elements to persist rather than seeing blank pages, we can achieve this by defining CSS View Transitions. With minimal code, we tell the current document page that when a view transition event occurs, keep the nav DOM element on screen. If any appearance delta exists between existing and destination pages, animate it with an ease-in-out transition:

@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; }
}

Suddenly, two different Workers feel like one seamless application.

Preloading with Speculation Rules

Transitioning between pages makes navigation look seamless—we also want it to feel as instant as a client-side single-page application. While Firefox and Safari don't currently support Speculation Rules, Chrome, Edge, and Opera do support this recent addition.

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.

Implementation requires defining a script rule in specific format telling supporting browsers how to prefetch other vertical slices connected to the web application, typically linked through shared navigation:

With this, applications prefetch other microfrontends and hold them in memory cache, so navigating to those pages feels nearly instant.

This isn't required for clearly discernible vertical slices like marketing, docs, and dashboard, as users would expect slight loads between them. However, it's highly encouraged when vertical slices are defined within specific visible experiences like dashboard pages.

Between View Transitions and Speculation Rules, we can tie together entirely different code repositories to feel as if they were served from a single-page application.

Zero-Config Request Routing

We need a mechanism to host multiple applications and stitch them together as requests stream in. Defining a single Cloudflare Worker as the "Router" provides a logical point at the edge to handle network requests and forward them to whichever vertical microfrontend is responsible for that URL path. Mapping a single domain to the router Worker makes everything else "just work."

Service Bindings

Cloudflare Worker service bindings allow one Worker to call into another without going through publicly-accessible URLs. A service binding enables Worker A to call methods on Worker B or forward requests from Worker A to Worker B.

The Router Worker can call into each vertical microfrontend Worker that's been defined (marketing, docs, dashboard), assuming they're all Cloudflare Workers. This is precisely the mechanism that "stitches" vertical slices together.

To define these microfrontends, update the Router Worker's wrangler configuration so it knows which frontends it can call:

{
"name": "router",
"main": "./src/router.js",
"services": [
{"binding": "HOME", "service": "worker_marketing"},
{"binding": "DOCS", "service": "worker_docs"},
{"binding": "DASH", "service": "worker_dash"}
]
}

This definition tells the Router Worker it can make requests into three separate additional Workers. Granting permissions is that simple.

Request Routing Logic

With knowledge of which Workers can be called, we need logic to direct network requests appropriately. Since the Router Worker is assigned to the custom domain, all incoming requests hit it first at the network edge. It determines which Worker should handle requests and manages resulting responses.

The first step is mapping URL paths to associated Workers. When certain request URLs are received, we need to know where they should be forwarded. We do this by defining rules:

/ = Marketing
/docs = Documentation
/dash = Dashboard

Each path needs mapping to an actual Worker. For the Router Worker, define a variable with this data to know which paths map to which service bindings:

{
"routes":[
{"binding": "HOME", "path": "/"},
{"binding": "DOCS", "path": "/docs"},
{"binding": "DASH", "path": "/dash"}
]
}

When a user visits /docs/installation, the request first reaches the Router Worker, which understands the /docs path prefix maps to the DOCS service binding pointing to worker_docs. The Router Worker removes the /docs prefix from the path, forwards the request to worker_docs, and returns the response.

Why drop the /docs path? This implementation detail choice allows the Worker to handle requests as if called from outside the Router Worker. Each service might have its own individual URL where it can be accessed independently. When attached to the Router Worker, it automatically handles prefix removal so the service remains accessible from its own URL or through the Router Worker.

HTMLRewriter

Splitting frontend services with URL paths makes request forwarding easy, but HTML responses that don't know they're being reverse proxied through path components causes problems.

If documentation contains , and users visit https://website.com/docs/, loading logo.png would likely fail because the /docs path is artificially defined only by the Router Worker.

Only when services are accessed through the Router Worker do we need HTML rewriting of absolute paths so browser responses reference valid assets. Before returning responses to clients, we rewrite the DOM—where we see absolute paths, prepend them with the proxied path. Where HTML previously returned , we modify it to .

The Router Worker can automatically handle view transition and preloading logic using HTMLRewriter. In your Router Worker ROUTES variable, setting smoothTransitions to true at root level automatically adds CSS transition view code. Setting the preload key within a route to true automatically adds speculation rules script code.

Example:

{
"smoothTransitions":true,
"routes":[
{"binding": "APP1", "path": "/app1", "preload": true},
{"binding": "APP2", "path": "/app2", "preload": true}
]
}

Get Started Today

You can start building with the Vertical Microfrontend template today. Visit the Cloudflare Dashboard or navigate to Workers & Pages and click Create application. Select "Select a template" then "Create microfrontend" to begin configuring your setup.

Check the documentation to see how to map existing Workers and enable View Transitions. We can't wait to see the complex, multi-team applications you build on the edge.

Source: Building vertical microfrontends on Cloudflare's platform - Cloudflare Blog