A frontend architectural analysis and implementation to introduce a micro frontend solution tailored to Facile's business needs.

Facile.it features several landing pages to promote its main products (insurance, mortgages, etc.). These pages are composed of several “blocks” containing static information from a CMS. They then link to interactive sections of the website where users can enter their information to receive a quote.
For UX and SEO reasons, Facile.it wanted to allow users to start interacting directly from the landing pages, integratingdynamic forms within the landing page blocks.
However, this posed a challenge: the landing pages and the dynamic pages are managed by different teams. How couldone team “provide” a dynamic section so that it could be included in the landing page by another team?
The solution we needed allow all teams to independently evolve their own part of the product, while providing flexible integration points.
To recap, we have:

Micro frontends immediately seemed like an obvious solution to the problem: we have multiple “frontends” to integrate, managed by different teams; that’s why this architectural pattern was born.
However, micro frontends come in many shapes and forms, and not all of them are suited for the specific business needs. Specifically, in the context of Facile.it, it was absolutely key to:

For example, many popular micro frontend solutions, such as single-spa,, require strong coordination between front-end applications, e.g., by enforcing the use of a specific build tool like Webpack. The Facile.it team initially explored this solution, but opted against it due to the technical constraints it would have imposed on the development teams.
After an initial analysis, Buildo envisioned a new architecture that builds on several proven technologies combined in a novel way—something that didn't exist off the shelf and was perfectly tailored for Facile.it's business needs.
We then built a working MVP of the solution: we did not stop at prototyping; instead, we worked directly with Facile.it’s technical team to deliver value immediately.
We documented the solution, created specific artifacts to accelerate adoption across other internal teams, and supported Facile.it’s tech leads in making the new solution production-ready.
Adding to what we discussed above, we benefited from specific technical constraints that limited the scope of our analysis:
The main challenges we wanted to address were:
Secondary challenges were:
Due to its architecture, Next.js can only output entire applications that take over the entire dom.
We then needed a way to bundle a single component and its related assets and serve them via Next.js.
The solution we eventually landed on uses Vite with a custom backend integration: this way we could “package” existing React components into standalone applications that could be server-side rendered and then served over the network.
Once we validated that the solution was working, we could package everything into a custom Vite plugin to hide the complexity and make it straightforward for each team to configure. The resulting Vite configuration would look something like this:
// vite.config.ts
import { defineConfig } from 'vite'
import { facileItMarkup } from '@facile-it/markup-vite-plugin'
export default defineConfig({
plugins: [
facileItMarkup({
blockIds: ['my-dynamic-block'],
})
],
})Similarly, we provided a utility library to serve the component using Next.js’s route handlers, and again, our target was to make the integration as concise as possible, without leaking the internal complexity:
// src/pages/api/markup/my-dynamic-block/index.ts
import { makeMarkupHandler } from '@facile-it/markup-utils/server'
import { render } from './entry-server'
export default makeMarkupHandler(
req => {
// Parse the request and return the parameters needed by the render function, if any.
// You can return an empty object or null if no parameters are needed.
return { someParam: req.query.someParam, someOtherParam: req.query.someOtherParam }
},
params => render(params, blockId).html,
'my-dynamic-block'
)In the Next.js app rendering the landing pages, we retrieved the URL of the dynamic block from the CMS and “materialized” it by fetching its text content.
This gives us the server-side rendered HTML of the block, which we then can include in our own landing page using the dangerouslySetInnerHTML functionality from React:
// __html here is the server-side rendered content retrieved by the block URL
return <div dangerouslySetInnerHTML={{ __html }} suppressHydrationWarning />This way, the HTML content of the dynamic block is inserted right away during the first server-side render of the landing page, satisfying our main SEO requirement.

Including multiple dynamic blocks without a bundling step presented another challenge: each block, as well as the host application, would include its own version of React and other common libraries, such as the design system component library.
Our two goals here were:
The solution we landed on leverages the import module, which is widely available across major browsers.
The general intuition is that instead of bundling the shared dependency, the host application and all the blocks would instead import the dependency from a common url, specifically an ESM-capable CDN, like https://esm.sh.
Concretely, this means applying a transform step to both blocks and host apps such that an import like
import { useState } from 'react';would become
import { useState } from 'https://esm-cdn.facile.it/react@18.3.1'The interesting bit to notice here is that browsers automatically cache and de-duplicate imports from the same module, so we gain these properties:
This meets our goals: optimize whenever possible, while still allowing flexibility to the different teams.

At Buildo, we always look beyond technical trends and focus on our clients' business needs; this allows us to craft tailor-made solutions that truly meet their goals.
In this case, we chose to discard well-established solutions like Single SPA or Module Federation as they would have introduced too many compromises for Facile.it. Instead, we used our deep knowledge of frontend architectures to compose lower-level components into a well-crafted solution that’s tailored to their specific requirements.
The solution has since been fully integrated, released to production, and is performing as envisioned, which is ultimately the best metric for determining its success.
Stai cercando un partner affidabile per sviluppare la tua soluzione software su misura? Ci piacerebbe sapere di più sul tuo progetto.