How does Mitosis compile components to different frameworks?
Updated
Mitosis is a tool from Builder.io that lets you write a component once in a JSX-like syntax and compile it into native code for React, Vue, Svelte, Angular, Qwik, and other frameworks. You get a single source of truth for a design system, and the compiler handles the framework-specific translation. This page explains the mechanism, using the official example as a reference.
The core idea: one source, many outputs
Instead of maintaining separate component libraries per framework, you write the component in mitosis.jsx using imports from @builder.io/mitosis. The compiler then reads that file and emits equivalent components in each target framework. The logic (state, props, event handlers) and styles are preserved, but the syntax is rewritten to match each framework's conventions.
What the input looks like
The source component uses JSX plus Mitosis primitives such as useState:
import { useState } from "@builder.io/mitosis";
export default function MyComponent(props) {
const [name, setName] = useState("Steve");
return (
<div>
<input
css={{ color: "red" }}
value={name}
onChange={(event) => setName(event.target.value)}
/>
Hello! I can run natively in React, Vue, Svelte, Qwik, and many more frameworks!
</div>
);
}
Two things matter here: the reactive state (name / setName) and the inline css object. Both need to survive compilation into each framework's own idioms.
How the compiler translates it
The compiler parses the JSX and maps Mitosis constructs onto each target's equivalents:
- State —
useStatebecomesrefin Vue, a class field in Angular, aletbinding in Svelte, anduseStorein Qwik. - Event handlers —
onChangebecomes@changein Vue,bind:valuein Svelte,onChange$in Qwik, and so on. - Styles — the
css={{ color: "red" }}object becomes a scoped class or inline style appropriate to the framework. - Markup — JSX is rewritten to templates (Vue, Angular, Svelte) or kept as JSX/TSX (React, Qwik).
The same component in each framework
The example shows one source producing five different outputs. The behavior is identical; only the syntax differs.
| Framework | State mechanism | Event binding | Style handling |
|---|---|---|---|
| React | useState |
onChange |
inline css |
| Vue | ref |
@change |
<style scoped> |
| Angular | class field | template binding | styles: [...] |
| Svelte | let |
bind:value |
<style> |
| Qwik | useStore |
onChange$ |
useStylesScoped$ |
For example, the Vue output uses ref("Steve") and a scoped .input { color: red; } rule, while the Qwik output uses useStore({ name: "Steve" }) and a scoped class name generated by useStylesScoped$. The Angular output declares name = "Steve" as a class property and moves the style into the component's styles array.
Why this matters for design systems
If you maintain a component library that must work across multiple frameworks, writing and testing each version separately multiplies the maintenance cost. Mitosis lets you keep one canonical implementation and regenerate the rest. The trade-off is that you work within Mitosis's supported subset of JSX, so very framework-specific features may not translate cleanly. For shared, presentational components and design-system primitives, that subset is usually enough.
Getting started
The site offers a Get Started path and a Playground where you can paste a Mitosis component and inspect the generated output for each framework. The Playground is the fastest way to verify how a given construct compiles before committing to it in a real project.