MIT licensed · GitHub →

Use HTML to compose
your React app.

Reactolith hydrates HTML into React. Any tag with a hyphen becomes a React component. Everything else stays plain DOM. Works with any backend that returns HTML — or no backend at all.

Get started →
The idea

It's just HTML with special tags.

Your backend renders HTML. Reactolith picks up any tag with a hyphen and renders it through React. Navigation, forms and SSE updates all use the same path: HTML in, React reconciler out.

HTML is the wire format

Return HTML from your existing controllers. Tags like <my-button> become React components. The rest is plain DOM.

State survives navigation

The built-in router fetches the next page and lets React reconcile against the live tree. Component state, focus and scroll stay where they were.

Real-time, same path

Mercure SSE pushes HTML updates through the same render pipeline. The DOM updates, React state stays.

Example

Two files.

The HTML on the left is whatever your backend already renders. The TSX on the right maps tag names to React components.

index.html — from your backend
<div id="reactolith-app"> <h1>Hello, {{ user.name }}</h1> <app-counter start="3"> <ui-button variant="primary"> Increment </ui-button> </app-counter> <form action="{{ path('save') }}" method="post"> <app-autosave-input name="title" /> </form> </div>
app.tsx — your React
import { App } from "reactolith"; import { Counter } from "./components/counter"; import { Button } from "./ui/button"; new App(({ is }) => { switch (is) { case "app-counter": return Counter; case "ui-button": return Button; case "app-autosave-input": return AutosaveInput; } });

Any tag with a hyphen → React component. Everything else → native DOM. How it works →

Features

What's in the box.

Backend-agnostic

Any backend, no backend, or a static file on a CDN.

Backend helpers, intact

Path helpers, permission checks, locale switches keep working.

State preserved

Navigate between pages, React state stays.

Live forms

Server can add or remove fields without losing focus.

Lightweight

Small runtime, no compiler step required.

Real-time

Mercure SSE pushes HTML through the same render path.

Scroll restoration

Back-button behaves like a regular browser.

IDE autocomplete

Generate web-types for JetBrains and VS Code.

Compared to

Inertia, htmx, Hotwire, SPAs.

reactolith Inertia.js htmx Hotwire / Turbo Full SPA
Returns HTML from backend ● ○ ● ● ○
Real React components ● ● ○ ○ ●
State across navigation ● ◐ ○ ◐ ●
Reuses backend helpers ● ○ ● ● ○
Build step required no yes no small yes
Replaces your router no yes no yes yes

More detail in the comparisons section.

Get started.

One npm install, a few lines of glue, and your backend can render React.

Install reactolith → GitHub