Intro to Mitosis: The universal reactive transformer6 min read
Mitosis.js is a compiler tool that consumes a universal component syntax and outputs framework-specific code. That means you can write application functionality once and generate it to React, Svelte, or Angular, among others. What distinguishes Mitosis from other compile-time frameworks is its “write once, run anywhere” approach to compilation. Mitosis is an impressive engineering feat, and it has applications anywhere you need to abstract front-end frameworks into pluggable components.
What is Mitosis.js?
Mitosis is a project from the folks at Builder.io, who also developed envelope-stretching projects like the Qwik.js framework and Partytown. Builder is itself a good example of the kind of application that benefits from Mitosis. In short, Builder allows you to visually design UI layouts across various underlying framework implementations. It needs a common language to process and output those diverse frameworks, and that language is Mitosis.
Mitosis also supports outputting to straight HTML and has Qwik.js on the roadmap. Also, Mitosis is the translation bridge that Builder uses between third-party design tool Figma. That is to say, the abstraction layer is useful in taking design output and transforming it into the desired target framework.
Listing 1 is a simple example that shows off a few conventions of Mitosis’s JSX variant.
Listing 1. Basic list output in Mitosis (TypeScript)
import useStore from '@builder.io/mitosis'; type Props = writer: string; ; export default function SongList(props: Props) const state = useStore( songs: [ title: "Strawberry Fields", writer: "John Lennon" , title: "Penny Lane", writer: "Paul McCartney" , title: "Dark Horse", writer: "George Harrison" , title: "It don't come Easy", writer: "Ringo Starr" ], ); return ( <div> <For each=state.songs>(song, index) => <div>song.title</div></For> </div> );
Listing 1 takes a list of objects (
songs) and outputs a property from each one (
song.title). There are few things to note in this sample. First, the file exports a default function. Therefore, it is defining a functional component. Mitosis will transform this component to the right structure for its target framework.
Next, note that the component utilizes a hook,
useStore. This hook works analogously to the one found in React. The code then uses the state to iterate over the songs with a
<For> component. Iterating over collections is one of those areas of diversity in frameworks and the
<For> component offers a simple, unified way to express it.
Also, observe the standard handling of component properties, via the
props argument to the function (with its attendant TypeScript type definition of
Run the compiler
To put this component through the Mitosis compiler (or, strictly speaking, transpiler), we can set up a simple Node Package Manager (NPM) project. To start, initiate a project (
npm init), then install the Mitosis libraries by entering
npm install @builder.io/mitosis-cli @builder.io/mitosis
The Mitosis compiler will automatically find the files we want to compile based on a
mitosis.config.js file. We’ll use the simple one shown in Listing 2.
Listing 2. Mitosis.config.js
module.exports = files: 'src/**', targets: ['vue3', 'solid', 'svelte', 'react', 'angular'], ;
Listing 2 tells where the sources are to be found (
src/**) and what output frameworks to use.
Our component is in TypeScript, so we’ll need a simple
tsconfig.json file, as well:
Listing 3. tsconfig.js
"compilerOptions": "jsx": "preserve", "jsxImportSource": "@builder.io/mitosis"
Listing 3 tells the TypeScript command-line interface (
tsc-cli) how to handle the JSX it encounters. In this case, it leaves the syntax as-is (
preserve) and defines the module to use for import (
@builder.io/mitosis). See the JSX overview and code sample in the TypeScript documentation for details.
Mitosis with React
Now we’re ready to get some output. Run
npm exec mitosis build. This will drop files into the
output/ directory, one branch for each target framework. Let’s take a peek at the
/output/react/src/components/Songs.jsx version, which will look something like Listing 4.
Listing 4. React version of Songs.jsx
import * as React from "react"; import useState from "react"; function SongList(props) const [songs, setSongs] = useState(() => [ title: "Strawberry Fields", writer: "John Lennon" , title: "Penny Lane", writer: "Paul McCartney" , title: "Dark Horse", writer: "George Harrison" , title: "It don't come Easy", writer: "Ringo Starr" ]); return /* @__PURE__ */ React.createElement("div", null, songs == null ? void 0 : songs.map((song, index) => /* @__PURE__ */ React.createElement("div", null, song.title))); export SongList as default ;
So, we can see that Mitosis has switched to using the React implementation of
useState and has opted for using
React.createElement to define the
song.map() to iterate over the collection. It exports the component as a default module. This looks like valid React so far, but let’s check it.
We can go to another directory and spin up a
create-react-app real quick (see the Create React App page for details), then go to the new directory that has just been created. In the
/src directory, we’ll copy over the
output/react/src/components/Songs.jsx file from our Mitosis project. We open
App.jsx and import the new component by adding
import “./Songs.jsx” as
Songs, then go into the template markup and use the component somewhere with
Now, we can run the app with
npm start. Check the output at
localhost:3000 and you’ll see the list of song names on the page.
Nice. Now we know that Mitosis is working with React. In a real-world situation, we could readily build a pipeline to add Mitosis to our build process.
Mitosis with Svelte
Let’s use a quick shortcut to see how Mitosis works with Svelte. Copy the contents of
/output/svelte/src/components/Songs.svelte (noticing that Mitosis has given the proper extension to the file). Go to the Svelte playground and paste the source into the left-hand code panel. After a moment, you will see the song list on the right side of the screen.
Mitosis is generating correct Svelte. If you’re curious, Listing 5 shows the idiomatic Svelte iteration for the
Listing 5. Song iterator in Svelte
#each songs as song, index <div>song.title</div> /each And Vue, Angular, SolidJS
You can take similar steps to verify the correctness of each of the other output targets.
Configuration and plugins
Mitosis is intended to be quite flexible. In particular, the Mitosis playground demonstrates the ability to change a configuration to select not only different frameworks but different characteristics within them. For instance, you can pick a state provider in React, choosing between
useState, Mobx, and Solid. You also can select different styling solutions, like Emotion CSS, Styled Components, and Styled JSX.
Mitosis also supports the ability to define plugins that run arbitrary code at strategic moments, like before and after the underlying JSON data structure is generated.
Consuming framework code
You might wonder if it is possible to flip Mitosis’s functionality from producing to consuming framework code. As an example, could we take a UI defined in a framework implementation and parse it into the Mitosis JSON model? That would not only let us two-directionally translate between Mitosis and a framework but actually translate between different frameworks via the Mitosis model.
I asked Builder.io’s founder, Steve Sewell whether Mitosis could understand framework code. Here’s what he said:
[Framework parsing] is definitely the biggest request we get. Right now most frameworks are a bit too freeform (not enough constraints) to do this reliably. That said Svelte is the best candidate for that, which is actively being worked on, we call it sveltosis.
Copyright © 2022 IDG Communications, Inc.