Do you have a large product built by many teams? Are you struggling to release often? Did your frontend turn into a massive unmaintainable monolith? If, like me, you’ve answered yes to any of those questions, this talk is for you! I’ll show you exactly how you can build a micro frontend architecture with Remix to solve those challenges.
Scaling Up with Remix and Micro Frontends
AI Generated Video Summary
This talk discusses the usage of Microfrontends in Remix and introduces the Tiny Frontend library. Kazoo, a used car buying platform, follows a domain-driven design approach and encountered issues with granular slicing. Tiny Frontend aims to solve the slicing problem and promotes type safety and compatibility of shared dependencies. The speaker demonstrates how Tiny Frontend works with server-side rendering and how Remix can consume and update components without redeploying the app. The talk also explores the usage of micro frontends and the future support for Webpack Module Federation in Remix.
1. Introduction to Remix and Microfontains
In this talk, we will discuss how to use Microfontains in Remix. I have experience as a senior software engineer at Tractable and as a principal software engineer at Kazoo, where I led the microfontain effort.
Hey! Hi, everybody. And welcome to this talk, Scanning up with Remix and Microfontains, in which we will talk about how to use Microfontains in Remix. A little bit about me. My name is I'm a senior software engineer at Tractable, which is a cool company that does AI and machine learning for insurance. So we take cool tech in quite a boring market, and we tried to make it more modern. Before that, I was a principal software engineer at Kazoo, and at that company, I kind of led the microfontain effort. So I'm going to talk mostly about my work there.
2. Introduction to Kazoo
Kazoo is a better way to buy a used car. They buy used cars, sell them online, and deliver them to your door. You get seven days to try it, and if you don't like it, you can return it. It's like a traditional e-commerce website, but with high-value items.
So first, you're probably wondering, what's a kazoo? I probably thought about that. That's not the kind of kazoo we're going to talk about. I'm going to talk about this kazoo. Kazoo is a better way to buy a used car. It's basically, for people in the US, it's a clone of Carvanha, but in the UK. And what it does is, they basically buy used cars and then sell them online. They deliver it to your door, and you get seven days to try it. If you don't like it, you just return the car, no problem, no question asked. So it's just your traditional e-commerce. Like, for people that build like us, you look at this, you're like, oh, it's an e-commerce website. Yes, it's an e-commerce website. Except every item is like £10,000 or something, right? Cool.
3. Front-end at Kazoo and Introducing Tiny Frontend
Kazoo follows a domain-driven design approach, with each vertical slice owned by a specific team. Examples of slices include search and browse, my account, and consumer finance. Kazoo deploys separate applications independently, but all rooted on the same domain. They encountered issues with granular slicing and introduced horizontal slicing. The challenge of providing a horizontal slice is called horizontal slicing in the microfrontend world. To solve this, the speaker created a library called tiny frontend, which is an NPM package that fetches the latest deployed implementation at runtime.
So what does front-end at Kazoo look like? From the beginning, Kazoo has been following a domain-driven design approach, meaning each vertical slice of the problem is owned by a specific team and they own the whole stack for that specific problem. So they own the front-end, the back-end, the deployment, everything.
So what are some examples of slices, you might ask? For example, you might have search and browse, which is that you search for a vehicle, see the detail of a vehicle, all these kind of things. You would have my account that lets you see your account information and past orders, or consumer finance, which is the loan application online. So when you buy the vehicle, you can apply for a loan. And there's lots of forms that ask you lots of really fun things like where have you lived in the past three years. And that's a separate app, that's a slice of the problem of selling a car online.
So from an implementation perspective, you basically have the domain Kazuko UK and different separate applications which are deployed independently and all own a set of pages on the domain. So if you look at that from a remix perspective, that would be basically multiple remix apps deployed separately, like independently by teams, but all pointing on the same, like all being like rooted on same domain, if that makes sense. At Kazuko, they didn't use remix, they used Next because it was built before, but potentially could migrate to remix.
So that approach works great. And it's called, like, vertical slicing in the micro-fundament architecture, like this kind of each team on a set of pages. But we saw that sometimes it wasn't granular enough. So let's imagine a scenario we actually had at Kazuko, which is a consumer finance team, which provides this loan application thing is like, you know what? To boost conversion, because we want to sell more loans, we need to display a finance calculator on the detail of a given vehicle, the detail page of vehicle. Or for example, when you like place an order and you want to rescind your delivery in my account, you would want to see some kind of thing that lets you select an available slot. And there's a team that owns all the logistics of moving cars. And this is the one that knows about available slots and what the UI and UX should look like for people to be able to book slots. So they want to deploy their thing on my account. Finally, that one is something that almost everybody has, a header and a footer, which need to be consistent across the whole website. So if you're in a company that deploys a few apps in production and you have a consistent branding, like if you do this kind of vertical slicing already, then you probably have that problem already. Like you probably have a header and footer that everybody needs to depend on. And that problem of providing a slice of the page like this, a horizontal slice of the page is called horizontal slicing in the microfinance world. Yeah.
So first thing that comes to mind and as a front-end engineer, you're like, oh yeah, that can be an NPM package, right? Like I'm just going to publish a package on NPM, like in our internal private repository or something that contains a React component, that's a header and a footer and everybody will just install that in the app and just render it done, right? Yes, but we've just introduced a build-time dependency between two teams by doing this, meaning any change to the header by the team that owns the header needs updating and re-deploying of every application that depends on the header. So that means now our team that manages that content basically needs to, each time they do a change, need to go see every single team and say, please can you update and deploy your application again? Which is really painful and kind of like hinders team ability to deploy fast. So we kind of want that pattern of NPM package to work quite well. But we would want independent deploys. Introducing tiny frontend. So to solve that exact problem at Kazoo, I built a library that's called tiny frontend and basically aimed to solve that specific kind of microfrontend problem. What is the tiny frontend? It's an NPM package that fetches its latest deployed implementation at runtime.
4. Introduction to TinyFrontend
TinyFrontend is an opinionated implementation of the microfrontend architecture aimed at solving the slicing problem. It follows the principle of using the framework as a runtime glue, expecting the host to have React. The host should be a vanilla remix app or any vanilla app using React. TinyFrontend aims to be easy to consume, like a regular npm package, and promotes type safety and compatibility of shared dependencies at build time.
So imagine an NPM package that you insert in your project but doesn't contain the actual source of the React component but fetches the latest one at runtime. It's one opinionated implementation of the microfrontend architecture and it's aimed at solving the slicing problem. It's not the one true way to microfrontend like microfrontend is just an architecture. There's a lot of different solutions out there that are different tradeoffs. This is just one of them.
It's not aimed at solving the vertical slicing problem. So for example, it doesn't care about routing or anything like this. It's just here to provide the component at runtime. OK, a few guiding principles that tinyfrontend follows. Use the framework as a runtime glue. So the idea is, for example, you might have heard of web components or things like this, and there are quite a few solutions that let you use cross frameworks like an Angular app, consumer React component, or things like this. This is not what tinyfrontend wants to do. It wants to just say cool, you're going to deploy a React component, and your host is going to be a React app. It's like a library. You expect your host to have React. It could be Vue, it could be something else, but it's an assumption we're making. Why do you want to do that? Because having multiple frameworks running in the same page is not great for performance, so generally having a consistent framework for your whole company makes for a better user experience.
Second principle, you don't need anything special to consume a TinyFrontend. Yet again, some Microfrontend implementation will require you to have a big host application that knows a lot of things. Basically it's more like a framework than a library, and TinyFrontend is more like a library. The host should be just a vanilla remix app, or a vanilla whichever app that uses React. We don't want the host to have anything complicated. For them it should be really easy. It should be basically the same as consuming a regular npm package. Third one, be type safe. We like type safety. TypeScript is our friend. If we can provide the types of our component to the team that consumes it so they can make sure that their code is going to run against it at runtime, that'd be great. Ensure shared dependencies are compatible at build time. Because we're providing that component to the other team, our component might be using a version of React.
5. Dependency Management and Architecture Overview
The host application may have different versions of React. Automatic optin for non-breaking changes is ideal, where components can be updated without requiring any action from the teams using them. Manual optin for breaking changes involves communicating the need for changes in the codebase and updating the package version. The architecture consists of a host application, a tiny frontend composed of frontend components and a contract, and a Cloudflare API for deploying and updating bundles. The host application installs the NPM package, which provides a method to fetch the latest bundle version. The complexity of fetching and using the bundles is abstracted away in a library, making it easier to develop and deploy new versions of the frontend components.
The host application might have a different version of React. We would want some way of checking that those dependencies are compatible at build time. Then, automatic optin for non-breaking changes. This is the idea of, like, I have a header, it doesn't need any information, it's just a React component and the type is just, like, cool, it's a header, render it, it's going to show the header. Whenever someone change a copy inside the header, the color, whatever, the contract of that component doesn't change. So team that use the header should not have to do anything. If you refresh the page, you should see the new header when you push a new one.
Manual optin for breaking change means whenever a team, actually, that on the header, actually need to do something new to do that job. For example, we just translated, like, made our website internationalized and now the component need to know which language it need to display. Otherwise, it can't do the job anymore. Well, in that case, we'll need to communicate to the team that choose that component to be like, actually, no, please, you need to provide language as part of the contract to that component for me to do my job. And in that case, you do basically a new package version and ask teams to update manually and change the codebase because they actually need to change something in the codebase for the component to be able to work.
What does the architecture looks like? So, we have this amazing diagram that's, like, everybody's like, oh, my God, what is happening here? There's so many pink boxes. We'll go through them one by one. The first one, the host. So, the thing that's going to display that microphone. So, a remix app in this case. The second one is a tiny frontend. It's a piece of frontend, a header, a footer, some kind of component that's going to be consumed by the host. So, displayed inside the host application and composed of two folders, an app folder that contained the actual source of your React component and a contract, which is an NPM package that you're going to publish for people that want to consume that at runtime to be able to do so. And finally, because we're talking about deploying those bundles from this tiny frontend because we need to be able to change them, because not in the NPM package, they can change somewhere, they need to be somewhere to push those bundles to. And in this case, it's a small API built on Cloudflare and deployed on Cloudflare as a Cloudflare worker.
Cool. So, our example host, in this case, Remix, it's going to install the NPM package, which provides a method which is asynchronous, and when you call that method, it returns to you a React component that you can use in your application. When you call that method, what does it do? Well, it uses a tiny client library to call an API to get the latest version of the bundle. So, all the complexity of, like, how do I get a React component at runtime from a bundle that's deployed somewhere and everything, all of that is abstracted away in the library. So, when you build a new tiny frontend, you don't need to reinvent the wheel all the time and copy paste a lot of code. You can just have like basically everything is just abstracted away in a library that you use. So, whenever the team that has the tiny frontend, for example, the header team, have a new version of the header, they'll just, like, push to the repository, they'll have some kind of, for example, a GitHub action that's going to bundle the new component, and then deploy that bundle to this Cloudflare API. And it will, like, basically update the state there to say, this is now the new bundle.
6. Demo of TinyFrontend and Server-Side Rendering
Someone will then, like a user, for example, now come to our Remix app and refresh the page. Because the bundle has changed, we'll get the new bundle and we'll just render the app with the new component. Nice.
Okay. So now we're going to go to a small demo. Because it makes much more sense when you see it for yourself. Cool.
So here we have a tiny frontend app. That tiny frontend app is this, like, box here in pink. Everything inside that is a component that's deployed independently. And it's this code base. And everything outside is just a regular remix app.
You can see that I can press. And there's communication between the two. This is because this is just a regular React component at runtime. So the communication is just literally the remix app can pass callbacks. And you do the communication the way you usually do communication in a React app. So some Microflaren framework have buses and things like this. But here, because we use the framework as the glue, all we have to do is pass callbacks, for example, if you want to.
7. Consuming the Component in Remix
On the Remix side, we consume the component by using the loadTinyFrontEndServer function. This function is provided by the npm package installed on the remix app and returns a promise containing the component. We store the component in the global context, allowing us to render it on the server. The same process is followed on the client side before rehydrating the remix app.
Yeah, let's now go on the Remix side and see how we consume that component. So on the Remix side, if we go to our server-side rendered page that we just saw, here's the text and everything, we can see that we get this component and it's just a regular component. We can also see it as a type. So for example, it expects a name property. If we remove it, it's going to complain and say, you need to provide a name. And how do we get it? Well, we get it either from client or server, depending on whether we're on the client or the server, and for example, on the server, how is it loaded? We have a function called loadTinyFrontEndServer which is provided by that npm package we installed on this remix app, and it returns a promise of basically something that contains our component. And we just store it in the global context. That means on the server, we call this function initially, and once we awaited that function, we know the component exists on the server and we can actually render. On the client, we do the same thing, but we call the client one, and we do that before rehydrating the remix app. There's probably some optimization you could do around the page and everything, but, yeah.
8. Updating Component in Remix
We can change the text and color of the component without redeploying the Remix app. The component is updated by pushing changes to the repository, triggering the CI/CD pipeline, and deploying the new bundle. The Remix app automatically fetches the latest version of the component, resulting in the updated UI.
Cool. So, now, let's see it actually change. So, if we go to the source of this app and we go to our component, we are able to change, for example, the text here, say, Hello Remix Europe. And let's change also the color, so if we go here and we change this to be Rebecca Purple, then we'll be able to commit this. So, I've committed this change and I'm pushing. Yet again now, I'm not doing a change to the Remix application, I'm just changing that component only. So, I just pushed on the repository that contains the source for this component here, right? So, the Remix app doesn't get redeployed, nothing changes there, it's still like the same app. If we go to the actions, we can see that there's an action that started on GitHub Action. It's currently running the CI, so it's installing the dependencies to build the latest bundle of this component. It's deploying and it is now deployed successfully. So now if we go here and we refresh, we see that it's now the new component. So press remix Europe and the bottle is now purple and we didn't have to touch our remix app. Pretty cool, right?
9. Remix and Vertical Slicing
With remix, we can achieve independent deployment of apps while maintaining a federated remix app that works across boundaries. The speaker demonstrates a proof of concept and mentions the possibility of using Webpack module federation for a more framework-supported approach. In the demo, the speaker shows how navigation between apps, live reload, and action functions work seamlessly within the remix host app. The speaker concludes the talk and invites the audience to a Q&A session, encouraging them to follow on Twitter.
But what about vertical slicing you ask? With remix, can we do something about that? So I don't know if you remembered, but we had this very independent remix application, right? And ideally what we would want, like one of the problems we have with this is when people between applications, you would actually have to download React again, right? Like each app will ship its own React and remix and React Router and all those dependencies. So ideally, we kind of want people to be able to, like teams to be able to deploy their own app independently but at runtime it's one federated, like remix app that just works across boundaries.
So we would want some kind of remix host running that could save pages from both a remote A or remote B and handle everything for us. So I built a very little, very basic proof of concept that shows that it's rather easy to achieve that in remix. You mostly need to change some stuff in the remix source. Although, if you really feel adventurous, you can use my demo to actually like try to do it yourself. But if you wait, I've heard it's possible it might... like Webpack module federation, to remix and allow you to do that in a like more framework supported way.
Okay. Demo time for that thing. Cool. So here is my remix federation Playground app and we have two apps. We have a host and a remote. The host is the one currently running and you can see in its app in its roots, it only has an index with a link to a slash remote page which doesn't exist in this code base. We also have another code base called remix remote and that one contains a remote page which is there. What happens if I click on this link? Hey, I navigated from one app to the other one. Now, the really cool thing is this is actually a regular, like, there's no page reload, nothing, it's client-side, this is a regular remix navigation. So it behaves just as if the remote page was part of the remix host app. Even better than this, we have things like, for example, live reload works. So if I change the remix remote, it actually reloads inside the host app. And we have stuff, everything you expect from remix, like action function works also. So, for example, I submit this form. It's actually going to call this action, and that function is actually going to run inside the host app. I'm not going to get in details on how that works because I don't have the time. But if you search in the discussion on the remix GitHub, you might find how this works. And that was all. Now we're going to switch to Q&A. Thank you, everybody, for listening to my talk. Make sure to follow me on Twitter at Baron André. And yeah, thanks, everybody.
10. Slido Results and Usage of Micro Front Ends
We discussed the Slido results on the usage of micro front ends. 75% of people said no, while 25% said yes. The speaker emphasizes that not everyone needs to use micro front ends and that it depends on the company and its pain points. They share their experience of working at Netlify, where micro front ends are not used, and at Grainger, where they were used. The decision to use micro front ends depends on the company's architecture.
And yeah, thanks, everybody. Cheers. First, we're going to go over the Slido results that we asked at the beginning of your talk. So we're going to look at the results for are you using micro front ends at your company. I'm going to switch over to that view and see what the results are. So no, 75% of people said no to that. That's crazy. And then 25% of people did say yes. So is that surprising to you? I guess it depends what company you work for, right? I think not everybody needs to use micro front ends. Use micro front end if it actually solves a pain point in your company. So it's totally fine not to use them. So I guess it depends on the audience and where they're from and where they work, I guess. I guess that is very true. I currently work for Netlify. And we don't use micro front ends. But I worked for Grainger before and we did use them there. So it is very dependent on the company that you're working for and what their architecture is. So that makes total sense.
11. Favorite Remix Feature: Form and Mutations
12. Remix Architecture and Micro Frontends
Doesn't Remix's architecture with file based routing reduce the need for micro frontends? Well, it kind of depends. In the case of Kazoo, where multiple teams are working on the same codebase, using a mono repo would be the only way to achieve that with one single Remix application. However, a mono repo comes with challenges in terms of deployment and PR queue. So if you want to maintain an independent small repo for a slice of your application, micro frontends can still be useful with Remix.
We have our first audience question from Baymax, doesn't Remix's architecture with file based routing reduce the need for micro frontends? Well, I would say it kind of depends. So one of the pain points, in the example I was giving, which was Kazoo we're talking like a codebase where a lot of engineers are working on at the same time. Kazoo is probably, I don't want to say, like on the public websites, probably like at least ten teams of like seven people, eight people. So that's like a lot of people working on the same codebase. The only way you would be able to do that with one single Remix application would be a mono repo, which comes with its own set of challenges in terms of deployment, PR, queue and everything. So if you don't want to go through all of that, and you want to keep an independent small repo that you can deploy independently for a slice of your application, then you can't use just the Firebase routing because it's just a different codebase, right? Yeah. So I think that's when microflop can be useful still with Remix. Yeah. That makes a lot of sense because if you are working across multiple teams, you have to have like that mono repo set up, like you said, and you have to have like a pipeline getting the code out there. So you have to navigate that. And that's a lot of processes and stuff.
13. Remix's Future Support and Micro Front-End Usage
Chris asks about Remix's future support for Module Federation, but the speaker is not part of the Remix team and cannot provide specific details. They mention the possibility of collaboration between the Webpack Module Federation ecosystem and the Remix team. The speaker advises Chris to stay updated with Remix's RFC and pipeline. They also highlight that Tiny Frontend does not require framework support and can be used in Remix and other frameworks. However, mixing multiple frameworks can impact performance. The speaker suggests using one framework to minimize the performance cost. They share their experience of working with different frameworks within the same app and recommend using micro front-ends if it solves specific problems for the company.
So Chris asks, you mentioned towards the end of the talk that Remix might support some of this going forwards. Do you have any more specifics on that? So sadly, I'm not part of the Remix team. So I can't say for sure. But I think people in the Webpack Module Federation ecosystem, for example, Zack, I think I've been hinting that they would been working with the Remix team to maybe add support for Module Federation in Remix, the same way there is a plugin that he made to support Next Module Federation, Next.js Module Federation. So if that comes out of the box, then you'll get basically this kind of ability of deploying independent Remix up, but they behave as just one single federated up inside of framework. But I don't think there's a timeline for it.
Okay. Yeah. I wish that we had you on before we did the Fireside Chat with Chance because I could have asked Chance, but I think that they have their RFC and their pipeline that they're coming out with the new features public now. So we were talking about that earlier. So maybe just keep an eye on that, Chris, if you're looking for things that are coming in the future, and you can always maybe like file an issue or start a discussion on their repo about that too.
Yeah. One small thing to add also is the tiny front end stuff though doesn't require any framework support. So that works already in Remix and Next and other frameworks out of the box. It's just like federated pages, like this vertical slicing thing that you need to change Remix for. Yeah. I actually think another good point of using Remix is that something we talked about earlier is that it's taking this step to being platform agnostic, where you may be able to use other frameworks, and if you're using a micro-frontend architecture, being able to write other languages may be useful for your company, right?
Yeah. So that's a good point. Although I think I generally advocate to not use micro-frontends to like mix frameworks, unless you're doing some kind of migration thing where you're deciding to explicitly migrate from React to Zvelt, or you say this is not the language we want to use anymore. And in the meantime, while we're migrating, we're going to have both at the same time. The reason is, I think it is like allowing multiple framework does allow for like developer freedom basically, but it does come as a performance cost for your users, because then they have to do multiple frameworks on the client to make everything work together. So I think generally I'm like, try to use one framework, so you can do that the framework costs only once, if that makes sense? I think that does make sense. The reason I brought it up is because when I was at Grainger, we did have like, there were React, there was like jQuery, Hybris components, there was Svelte components. So we had like everything kind of working together and things had been either done by contractor companies or other things, other companies. And then our team was working on the Svelte piece and then they ended up switching to React. So we were kind of working with all of these within the same app and that might be like a future. So I was just wondering what your thoughts were on that, but I do agree, if you have the choice to just stick with one framework, it's much less overhead for your company.
So would you suggest that people use a micro front-end at their company? So I think Ruben Cazas, who is a person that also talks a lot about micro front-end, also have some talks on should you use micro front-end in your company? And I think I can agree with his opinion, which is use micro front-end if it solves a problem for your company. So if you have just a few teams and you can share monorepo and everybody can contribute and you don't have pain points there, just do a monorepo, do this and it's fine. If you start feeling the pain of like, we need to be able to deploy independently because we keep like stepping on each other's feet, it's not manageable.
14. Tiny Frontend and Webpack Module Federation
Tiny Frontend does not use webpack module federation. It uses VIT and specifies each shared dependency in the aspia dependencies. The library provides a lightweight solution by exposing dependencies on the global context. If webpack module federation becomes a standard, the internals of tiny front-end can be easily modified to use it.
Like the PRQ is getting crazy and everything. We want really to have this independence of deployment, then go for the like then micro front-end is a tool that can help you, you know, like remove those dependency or make them more, like, looser. So yeah.
It's like every other developer question. It depends. Yeah, exactly. Senior developers. Right.
Okay, so we have one more question for you. This is kind of going back to Baymax's question earlier with the webpack module federation. Is tiny front-end using webpack module federation? Good question. So no, it's not. It could, like the underlying implementation could use webpack module federation. When I built it, I decided not to. The main reason is then it's not using webpack to bundle, it's actually using I think ESBuild. I mean it's using VIT. And the main reason is in tiny front-end you specify each dependency you're sharing like in your like aspia dependencies. And basically what it does is just do a tiny bit of glue to like expose those on the global context and then like plug them in for you. And webpack module federation can do a lot of things for you, it can do like dynamic version like resolution and all this kind of things. And that wasn't really needed for tiny front-end because you cannot just have this one-on-one relationship between the host and the package that comes in. Which is why I didn't go with it, it's just a bit lighter. But to be fair you know like if for example webpack module federation becomes bundle-diagnostic and like can as a standard and everybody starts using it, then it would be rather trivial to change the internals of tiny front-end to like use that in the hood instead.
Yeah that is amazing, okay well I think that is all the time we have for today. Thank you so much Adrian for joining us for the Q&A and thank you for the talk.