A common challenge among development teams is using the same language; while one subteam is using Vue, another may be using React, causing redundant work and forcing you to create shared components twice.
In this tutorial, we’ll explore Mitosis, a tool that can compile code to standard JavaScript in addition to frameworks and libraries like Angular, React, and Vue, allowing you to create reusable components.
We’ll review some background information to clarify when you should use Mitosis, then install Mitosis in a new project to see it in action. Let’s get started!
While some developers initially turned to web components to create reusable components, they encountered issues like web components’ browser compatibility and its lower-level approach, which made the creation of reusable components an intensive process.
Similarly, certain libraries like React had workflows that made incorporating web components difficult, leading to the creation of solutions like Google’s LitElement, Ionic, and Stencil.js.
Meanwhile, a parallel trend arose in Svelte and SolidJS. Intended for building applications, Svelte and SolidJS made huge performance gains by compiling the source code to standard JavaScript, creating bundles that were smaller and faster than web components.
Mitosis builds on the functionality of Svelte and SolidJS, employing the same compilation speed and allowing you to recompile a component into different frameworks and libraries from the same codebase. The compiled component operates like any other component in the framework.
Similar to SolidJS, Mitosis uses a version of JSX that compiles the components to JSON. Plugins then compile the components to different targets, allowing you to create tooling in two directions:
For these reasons, Mitosis supports no-code tools. For example, Builder.io allows you to create your website using a visual tool, then compile it to the framework of your choice. Builder.io acts as a CMS but is powered by Mitosis.
Mitosis uses WYSIWYG editing and a SPA framework for compilation. Now that we understand how Mitosis works, let’s create a component with Mitosis.
First, we’ll install the Mitosis CLI:
npm install -g @builder.io/mitosis-cli
Create an empty folder on your computer. In your terminal, open the folder and create a new npm project:
npm init -y
Next, we’ll install Mitosis:
npm install @builder.io/mitosis
Create a file called component.lite.jsx
. lite.jsx
is the extension for Mitosis projects. Next, we’ll install the Builder.io VS Code extension, which provides syntax highlighting for lite.jsx
files.
In the component.lite.jsx
file, add the following code:
import { useState, Show, For } from "@builder.io/mitosis"; export default function Component(props){ const state = useState({count: 0})
In the code above, we declare state using the useState
Hook. Now, when any property in the state object is changed, the UI will render.
Next, add the following code block to component.lite.jsx
, which will return JSX like React or SolidJS:
return (<div> {/* DISPLAY SOME JSX CONDITIONALLY */} <Show when={state.count > 10}> <h1>You Win!!!</h1> </Show> {/* DISPLAY THE COUNT */} <h1>{state.count}</h1> {/* BUTTON TO ADD TO THE COUNT */} <button onClick={(event) => {state.count += 1}}>Click to Add One</button> </div>) }
In the code above, the show
component allows us to conditionally render the UI. Because state is updated with plain reassignments, there is no need to add the setState
function, which we’d use in React. Lastly, notice that all our state can be bundled into one object.
Now that our component is built, let’s see some examples of our component compiled to different frameworks!
Let’s use Mitosis to compile our component into a React component:
mitosis compile --to=react component.lite.jsx > component.jsx
The --to=
flag lets us select the framework that we’ll compile our Mitosis component to. When compiled to React, we’ll get the following output:
import { useContext } from "react"; import { useLocalObservable } from "mobx-react-lite"; export default function Component(props) { const state = useLocalObservable(() => ({ count: 0 })); return ( <div> {state.count > 10 && ( <> <h1>You Win!!!</h1> </> )} <h1>{state.count}</h1> <button onClick={(event) => { state.count += 1; }} > Click to Add One </button> </div> ); }
--to=vue component.lite.jsx > component.vue
When compiled to Vue, our component will look like the code below:
<template> <div> {{/* DISPLAY SOME JSX CONDITIONALLY */}} <template v-if="count > 10"> <h1>You Win!!!</h1> </template> {{/* DISPLAY THE COUNT */}} <h1>{{ count }}</h1> {{/* BUTTON TO ADD TO THE COUNT */}} <button @click="count += 1">Click to Add One</button> </div> </template> <script> export default { name: "Component", data: () => ({ count: 0 }), }; </script>
--to=svelte component.lite.jsx > component.svelte
When we compile our Mitosis component to Svelte, we’ll get the following output:
<script> let count= 0 </script> <div > {#if count > 10 } <h1 >You Win!!!</h1> {/if} <h1 >{count}</h1> <button on:click="{event => count += 1; }" >Click to Add One</button> </div>
--to=angular component.lite.jsx > component.tsx
When we compile our Mitosis component to Angular, it will look like the code below:
import { Component } from "@angular/core"; @Component({ selector: "component", template: <div> <ng-container *ngIf="count > 10"> <h1>You Win!!!</h1> </ng-container> <h1>{{count}}</h1> <button (click)=" count += 1; " > Click to Add One </button> </div> , }) export default class Component { count = 0; }
--to=customElement component.lite.jsx > component.js
When compiled to web components, we’ll get the following output:
/** * Usage: * * <component></component> * */ class Component extends HTMLElement { constructor() { super(); const self = this; this.state = { count: 0 }; // Event handler for 'click' event on button-1 this.onButton1Click = (event) => { this.state.count += 1; this.update(); }; } connectedCallback() { this.innerHTML = ` <div> <span data-name="show"> <h1>You Win!!!</h1> </span> <h1> <span data-name="div-1"><!-- state.count --></span> </h1> <button data-name="button-1">Click to Add One</button> </div> <style></style>`; this.update(); } update() { this.querySelectorAll("[data-name='show']").forEach((el) => { el.style.display = this.state.count > 10 ? "inline" : "none"; }); this.querySelectorAll("[data-name='div-1']").forEach((el) => { el.innerText = this.state.count; }); this.querySelectorAll("[data-name='button-1']").forEach((el) => { el.removeEventListener("click", this.onButton1Click); el.addEventListener("click", this.onButton1Click); }); } } customElements.define("component", Component);
As you can see, it is straightforward to install Mitosis, create a component, and then compile it to the language, library, or framework of your choice. We covered several examples in this tutorial, but only scratched the surface; other compilation targets include Swift, Liquid.js, SolidJS, React Native, and more.
When it is impossible to use the same language as the rest of your team, Mitosis is a helpful tool that saves time by reducing redundant work.
Mitosis allows you to write one codebase for an individual component then compile it to one of many targets. It facilitates low-code and no-code solutions for creating fast, reactive applications.
We explored the Builder.io plugin, but another popular plugin is the figma-html plugin, which allows you to turn your Figma design into code for any framework.
As you build your own reusable components, you can see the visual results using the JSX-lite fiddle from Builder.io. However, at the time of writing, this feature is still in an early preview stage. I hope you enjoyed this tutorial!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowJavaScript’s Date API has many limitations. Explore alternative libraries like Moment.js, date-fns, and the new Temporal API.
Explore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.
2 Replies to "Create reusable components with Mitosis and Builder.io"
I’ll check this out, thanks. đź‘Ť
In Svelte you can compile components to regular web components, and use regular web components with ease in Svelte projects or other frameworks. We’ve had tremendous ease and great success with that approach. I’ll evaluate this to see how it compares. Thanks for the heads up and writeup. Cheers.
web components can have trouble with SSR