Better Micro Frontends and Design Systems with Independent Components

Why you should stop using tools and methodologies made for monolithic projects to develop and collaborate on modules.

Eden Ella
Bits and Pieces

--

Independent components imported from multiple scopes and exported back with a new version

Micro frontends and design systems aim to solve different sets of challenges. However, as you’ll soon see, they are both flawed for the same reason. Both are developed and delivered using the same improper tools and methodologies that presuppose a monolithic project. This article will explore independent components as a radically different design pattern that solves many of their common issues.

Independent components are components and modules that are developed and versioned independently and shared across remote ‘scopes’ (more on that later). They are authored and composed together using Bit.

Independent components are all part of a single virtual monorepo. That means an update made to one component will propagate to all its dependents using a CI for components (each component with its own CI). That makes collaboration, in-and-across projects, simple and safe.

Also Read:

Micro Frontends

MFEs is an architectural style that aims to optimize frontend development. It does so by breaking up the frontend into small and decoupled codebases. These codebases are easier to understand and maintain. They enable parallel independent builds and deliveries, and more effective collaboration between teams working autonomously.

Varying on the type of MFEs pattern, secondary goals may include smaller bundles for initial loads (when integrating MFEs in client runtime), incremental updates, and the freedom to use different technologies and frameworks in a single app.

Not everyone roots for MFEs

The MFEs architecture is a controversial one, though it seems it is mostly due to one specific use-case, the integration of different technologies and frameworks in a single app.

Those who oppose it are right to do so. What works well for backends (i.e, microservices) doesn’t play that well for frontends, with shared styling, global states, shared dependencies, and other global properties of a single frontend app.

In fact, MFEs, in most cases, require a way to share code and third-party libraries. Otherwise, a single frontend app will look more like a mosaic than a coherent interface. It will contain many duplications of code, and will most probably encounter errors with libraries requiring just a single instance to function properly (i.e peer dependencies like React, Redux, etc.)

In short, multiple technologies mean more to maintain and a higher probability for runtime conflicts (a.k.a. bugs in production 🙂). A good MFEs architecture is one that limits duplication of code, shares third-party libraries, and maintains consistency in UI.

Component libraries/design systems

Design Systems are often implemented as Component Libraries, to enable cross-project code reuse and standardization of technology, design patterns, and “looks and feel”.

The gap between the ideal and reality

Component Libraries have their own drawbacks. They’re not very effective in accelerating development as they offer simple and quite abstract components (this is so, in order for them to be as composable as possible).

Furthermore, they often enforce opinions that are inadequate to the hosting project, so much so that the team building that project prefers not to use them at all. This, in turn, decreases the level at which a design system is adopted in an organization and opens up the door to all sorts of trouble with code duplication and total lack of standardization.

The solution: independent components

The solution to the challenges presented by both MFEs and DSs can be found with independent components and Bit.

Independent components are components that are independently developed, versioned, and collaborated on. They can be JavaScript modules, React components, React hooks, CSS modules, etc.

An independent React (page) component

Most importantly, independent components, although distributed, form a network of dependencies between themselves. This network of dependencies is used by Bit, and on a larger scale, by Bit.dev, to propagate a CI from a single modified component to all its dependents, in-and-across projects.

Independent components are authored in Bit workspaces, which provide all the tooling needed for components to be independently developed and versioned, with minimal effort. That includes isolated rendering, pre-configured (and extensible) component development environments, auto-generated dependency graphs, and much more.

A ‘dropdown’ React component explored in Bit’s workspace UI. Left: the component rendered in isolation. Right: the component’s runtime dependency graph.

Once an independent component is released with a new version, it carries the “genetic code” for its runtime environment, that is, its runtime dependency graph., and the “genetic code” for its development environment. That includes the components’ dev dependencies and their specific configurations.

That means an independent component can be cloned into a new Bit workspace where it can be developed, without losing any time setting up its dev environment.

An independent component contains the entire version history of its source code, configurations, and artifacts.

As you might have guessed, a Bit workspace is not limited to just a single component. That means, multiple independent components, with different development environments and CIs, can all be developed and composed together in a single workspace.

Using IC for design systems and Micro frontends

Independent components allow us to take component-driven development much more seriously. We can compose all our web projects out of shared components, from the very elementary ones, all the way to the very large and complex ones (i.e, MFEs and apps). This makes the borders between micro frontends, design systems, and even components and apps, almost completely fade away.

A design system out of a network of independent components

An independent component can play the role of a component in a design system/component library (as it also provides an auto-generated distributable Node package).

Unlike a traditional design system, this network of independent components, making up the “design system”, is limitless. A wider selection of shared components makes this “design system” much more effective in accelerating development, as consumers of these components can find exactly what they need for their new composition. This, in turn, enforces standardization with the minimal needed adaptations to fit a specific project (or set of projects).

Micro frontends as independent components

An independent component can also play the role of a micro frontend as it has its own CI/CD. That means it can be integrated not only at build-time but also at client runtime and server runtime. That being said, for most cases, it is better to integrate MFEs at build time. That allows for teams to work independently (arguably, the main purpose of MFEs) while maintaining a robust app, with shared components and libraries.

An MFE component is not only built as an independent component but is also composed out of independent components. That means consistency between MFEs is maintained using shared components. And, whenever one of these shared components is updated, all MFEs will be tested as well. That adds robustness to the entire (distributed) project while maintaining an excellent UX.

Conclusion

Independent components are not just another web technology but rather, a complete paradigm shift. They fundamentally alter the way we think of web development and drastically change the way we collaborate.

See here, to learn more about Bit and independent components.

You can also check out Bit.dev, to learn more about its component hosting and CI solutions.

--

--