
A new Worker template for Vertical Microfrontends (VMFE) is now available. This template enables the mapping of multiple independent Cloudflare Workers to a single domain. This allows teams to operate in silos, independently deploying marketing, documentation, and dashboard sections, while presenting users with a unified application experience.
Traditional “horizontal” microfrontend architectures fetch different parts of a single page from various services. Vertical microfrontends, however, divide applications by URL path. For instance, a team managing the /blog path is responsible for the complete vertical stack for that route, including framework, library choices, and CI/CD. This full ownership of a path’s stack empowers teams to confidently deploy their work.
As teams expand, challenges arise from diverse framework needs; for example, Astro might suit a marketing website better, while React could be ideal for a dashboard. In monolithic codebases, collective deployments by multiple teams can lead to frustrating rollbacks if one team introduces a regression. The goal is to hide technical implementation details from users while allowing teams to deliver a cohesive user experience with full autonomy over their domains.
Vertical microfrontends offer a solution. This article explores how they address common developer challenges.
What are vertical microfrontends?
A vertical microfrontend is an architectural pattern where an independent team manages a complete segment of an application’s functionality, from the user interface to the CI/CD pipeline. These segments are defined by domain paths, allowing individual Workers to be associated with specific paths:
/ = Marketing
/docs = Documentation
/blog = Blog
/dash = Dashboard
Further granularity can be achieved by associating Workers with sub-paths, particularly within a dashboard. Features or products within a dashboard are often segmented by adding depth to the URL path (e.g., /dash/product-a), meaning navigation between two products could involve entirely different codebases. With vertical microfrontends, the following structure is also possible:
/dash/product-a = WorkerA
/dash/product-b = WorkerB
Each of these paths represents an independent frontend project, with no shared code. The product-a and product-b routes correspond to distinct frontend applications, each with its own frameworks, libraries, and CI/CD pipelines, managed by separate teams. This enables end-to-end code ownership. The next step involves integrating these disparate projects to create a unified user experience.
This challenge is also encountered within Cloudflare, where numerous teams manage individual products within the dashboard. Teams must address how external changes affect the user experience of their product. Internally, a similar strategy is now employed for the dashboard. When users transition from the core dashboard to the ZeroTrust product, they are actually moving between two distinct projects, with routing handled by the path /:accountId/one.
Visually unified experiences
Integrating these individual projects to create a unified experience is simpler than it might seem, often requiring only a few lines of CSS. It is crucial to avoid exposing implementation details and internal decisions to users. A failure to deliver a cohesive frontend experience would be a disservice to users. To achieve this seamless integration, understanding view transitions and document preloading is essential.
View transitions
For seamless and smooth navigation between distinct pages, view transitions are highly effective. By defining specific DOM elements to persist until the next page loads and specifying how changes are managed, view transitions become a powerful tool for integrating multi-page applications.
However, there are situations where distinct visual identities for various vertical microfrontends are acceptable. For example, a marketing website, documentation, and dashboard might each have unique designs, and users would not expect a cohesive feel when navigating between them. Conversely, if vertical slices are introduced within a single experience, such as a dashboard (e.g., /dash/product-a & /dash/product-b), users should remain unaware that these are separate repositories, Workers, or projects.
To demonstrate, achieving a unified feel for separate projects with minimal effort is possible using CSS View Transitions. These allow animated transitions between different views—whether in single-page (SPA) or multi-page (MPA) applications—to appear as a single experience. Without view transitions, navigating between pages managed by different Workers would result in a brief white screen, making pages feel disjointed and not like a single-page application.
Appears as multiple navigation elements between each site.
To prevent a blank white page during transitions, CSS View Transitions can be defined. The provided code instructs the current document to retain the navigation DOM element during a view transition event. Any visual differences between the current and destination pages are then animated with an ease-in-out transition. This makes two distinct Workers appear as a single entity.
@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; }
}
Appears as a single navigation element between three distinct sites.
Preloading
Seamless page transitions should also feel instantaneous, similar to a client-side SPA. While Firefox and Safari do not yet support Speculation Rules, Chrome, Edge, and Opera do support this newer API. The speculation rules API enhances performance for future navigations, especially for document URLs, making multi-page applications behave more like single-page applications.
In code, this involves defining a script rule in a specific format. This rule instructs supporting browsers on how to prefetch other vertical slices connected to the web application, typically those linked via shared navigation.
<script type="speculationrules">
{
"prefetch": [
{
"urls": ["https://product-a.com", "https://product-b.com"],
"requires": ["anonymous-client-ip-when-cross-origin"],
"referrer_policy": "no-referrer"
}
]
}
</script>
This allows the application to prefetch other microfrontends, storing them in an in-memory cache. Consequently, navigating to these pages feels almost instantaneous.
This feature may not be necessary for clearly distinct vertical slices (e.g., marketing, documentation, dashboard), as users might anticipate a slight load time. However, its use is strongly recommended when vertical slices are part of a unified visible experience, such as within dashboard pages.
By combining View Transitions and Speculation Rules, it is possible to integrate entirely different code repositories, making them appear as if they originate from a single-page application.
Zero-config request routing
A mechanism is needed to host multiple applications and integrate them as requests arrive. Designating a single Cloudflare Worker as the "Router" provides a central logical point (at the edge) to manage network requests. This router then forwards requests to the appropriate vertical microfrontend based on the URL path. A single domain can be mapped to this router Worker, simplifying the setup.
Service bindings
Exploring Cloudflare Worker service bindings is beneficial. These bindings enable one Worker to invoke another without using 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 B. Specifically, a Router Worker can call into each defined vertical microfrontend Worker (e.g., marketing, docs, dashboard), provided they are Cloudflare Workers.
This mechanism is crucial for integrating vertical slices. The next section will detail how request routing manages traffic distribution. To define each microfrontend, the Router Worker's wrangler definition must be updated to specify which frontends it can call.
{
"$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"
},
]
}
The sample definition provided is configured within the Router Worker, indicating permission to make requests to three additional Workers: marketing, docs, and dash. While granting permissions is straightforward, more complex logic involves request routing and HTML rewriting of network responses.
Request routing
Given the ability to call various other Workers, logic is required to direct network requests appropriately. When assigned to a custom domain, the Router Worker receives all incoming requests at the network edge. It then identifies which Worker should process the request and manages the subsequent response.
The initial step involves mapping URL paths to their corresponding Workers. Upon receiving a request URL, its destination must be determined. This is achieved by defining rules. While wildcard routes, dynamic paths, and parameter constraints are supported, this discussion will focus on literal path prefixes for clarity. For instance, three microfrontends are used in this example:
/ = Marketing
/docs = Documentation
/dash = Dashboard
Each of these paths must be mapped to an actual Worker (refer to the wrangler definition for services previously discussed). For the Router Worker, an additional variable is defined with data specifying which paths map to which service bindings. This enables routing users as requests arrive. A wrangler variable named ROUTES should be defined with the following content:
{
"routes":[
{"binding": "HOME", "path": "/"},
{"binding": "DOCS", "path": "/docs"},
{"binding": "DASH", "path": "/dash"}
]
}
Consider a user visiting the website path /docs/installation. The request first reaches the Router Worker, which identifies which URL paths correspond to individual Workers. It recognizes that the /docs path prefix is mapped to the DOCS service binding, which, according to the wrangler file, points to the worker_docs project. The Router Worker, aware that /docs is a vertical microfrontend route, removes the /docs prefix from the path and forwards the request to the worker_docs Worker for processing, then returns the resulting response.
The /docs path is dropped as an implementation choice. This allows the Worker, when accessed via the Router Worker, to process the URL as if it were called directly. Similar to any Cloudflare Worker, the worker_docs service can have its own independent URL. The design ensures this service URL continues to function independently. When integrated with the new Router Worker, the prefix is automatically removed, making the service accessible either through its own URL or via the Router Worker.
HTMLRewriter
Dividing frontend services using URL paths (e.g., /docs or /dash) simplifies request forwarding. However, issues arise when HTML responses contain relative paths that are unaware of being reverse proxied through a path component. For example, if a documentation website's response includes <img src="./logo.png" /> and a user visits https://website.com/docs/, the logo.png file might fail to load because the /docs path is an artificial construct of the Router Worker.
HTML rewriting of absolute paths is necessary only when services are accessed via the Router Worker, ensuring that browser responses reference valid assets. When a request passes through the Router Worker, it is forwarded to the correct Service Binding, and a response is received. Before this response is sent to the client, the DOM can be rewritten. This involves prepending absolute paths with the proxied path. For instance, an image tag like <img src="./logo.png" /> would be modified to <img src="./docs/logo.png" /> before being returned to the client browser.
Revisiting CSS view transitions and document preloading, while manual code insertion is possible, the Router Worker can automate this logic using HTMLRewriter. By setting smoothTransitions to true at the root level of the Router Worker's ROUTES variable, CSS transition view code is automatically added. Similarly, setting the preload key within a route to true automatically includes script code speculation rules for that route. An example demonstrating both features is provided below:
{
"smoothTransitions":true,
"routes":[
{"binding": "APP1", "path": "/app1", "preload": true},
{"binding": "APP2", "path": "/app2", "preload": true}
]
}
Get started
The Vertical Microfrontend template is available for immediate use. Access it via the Cloudflare Dashboard deeplink here, or navigate to "Workers & Pages", click "Create application", then "Select a template", and finally "Create microfrontend" to begin configuration.
Consult the documentation for instructions on mapping existing Workers and enabling View Transitions. Complex, multi-team applications can be developed at the edge using this functionality.

