Library / SDK
thoughtbot/superglue avatar
thoughtbot/superglue

Superglue gives a Rails monolith a React and Redux front end without a separate API

A productive library for Classic Rails, React and Redux

612 stars27 forksJavaScriptMIT

At a glance

What is it?
Superglue is a thoughtbot library that renders React and Redux pages from classic Rails views, using the same route to serve HTML or JSON depending on the request. The interesting part is PropsTemplate, a JSON templating DSL that lets a partial update name the exact node it wants back, and the harder part is that the whole approach depends on a Rails app you already have.
Who is it for?
Adopt Superglue if you have a classic Rails application and want to add interactive React pages without splitting the backend into a separate API service, because the HTML and JSON come from the same view and the same route. Do not adopt it for a greenfield application in 2026, because the repository has no GitHub releases and the current npm version is 2.0.0-rc.3, a release candidate, and because the whole premise is classic Rails.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 23 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One route, two representations, no API layer

Superglue's pitch is in the README's first line: use classic Rails to build rich React Redux applications with no APIs and no client-side routing. The mechanism it uses is not clever, it is just Rails' own respond_to, and that is the point.

In a Superglue application, if you direct your browser to /dashboard.html you see the HTML version of the content, and if you go to /dashboard.json you see the JSON version of the exact same content down to the footer. One route, one view, two renderings, chosen by mime type.

The consequence is worth spelling out because it changes what a developer has to build. There is no serializer layer to maintain, no separate API controller, no versioning discussion between a client team and a server team, and no client-side router because navigation happens through the server. What you get instead is a system where the same page template produces the initial HTML and the JSON that the React component hydrates from.

The comparison the README draws is with Hotwire, Turbo and Stimulus, and the claim is that Superglue makes React and Redux as productive as those. The inspiration is Turbolinks, and the design goal is to feel like a natural extension of Rails. The benefits it lists are keeping the productivity of Rails form helpers, UJS, tag helpers, flash and cookie auth while getting Redux state management and React components.

The directory layout shows a view that renders three ways

The README shows the shape of a Rails application that uses Superglue, and it is worth reading slowly because the three files in one directory are the whole design.

There is a controllers/ directory and a views/ directory, and inside views/ there is a dashboard/ directory holding index.jsx, which the README calls the React page component, index.json.props, which is the json for the page component, and index.html.erb.

So a single page is described three times over. The .jsx file is the React component that renders on the client. The .json.props file is the template that says what data the component needs. The .html.erb file is the server rendered version of the same content for the first paint.

The .json.props file is the piece that connects the two, and it is what the PropsTemplate section is about. Powering the JSON responses is PropsTemplate, a diggable JSON templating DSL inspired by JBuilder. With PropsTemplate you specify a path of the node you want, and PropsTemplate walks the tree to it, skipping the execution of nodes that don't match the keypath.

That skipping behaviour is the point of the design. A props template is not a dump of the page data, it is a query against the view tree, and the DSL is built so a partial update can ask for one node instead of serialising everything. The inspiration is JBuilder, which solves the same problem in the other direction, and the special thanks list also names scour, which is a JSON scrubber for exactly this kind of thing.

props_at is how you ask for one node instead of a whole page

The partial update story is the part of the README with a concrete example, and the example is a type-ahead search.

The traditional approach to type-ahead search in a Rails app is a fair amount of work. The README describes it as needing a new controller, routes, a discussion over versioning, a JSON serializer, plenty of new JS code, and so on.

With Superglue it is a single onChange handler:

js
import {NavigationContext} from '@thoughtbot/superglue'

const {remote} = useContext(NavigationContext)

const onChange = (e) => (
  remote(`/dashboard?qry=${e.target.value}&props_at=data.header.search`)}
)

The mechanics of what happens next are the interesting part. With props_at set to data.header.search, the request goes to /dashboard?qry=haircut, the server digs the template for the data.header.search node, returns it in the response, and the client immutably grafts it into the exact same path on the redux store before finally letting React re-render.

Two properties of that design are worth pulling out. The first is that the update is path-addressed. The client says which node it wants, and the server returns that node, so the response is proportional to the change rather than to the page. The second is that the graft is immutable and lands at the same path in the store, so the component tree does not need to know how the data arrived.

That last point is what makes the pattern compose with ordinary Redux. The store stays a normal Redux store with a normal shape, and the update mechanism is a specialised way of writing to it, not a second source of truth.

Navigation uses a UJS attribute and a pushState

Page to page navigation without a reload is the other feature, and it is built on the same idea of borrowing from the server rather than from the client.

Superglue has its own UJS attributes inspired by Turbolinks, and the example is short:

js
<a href="/posts" data-sg-visit />

The README describes exactly what happens. The link requests /posts with an accept of application/json, and when the client receives the response, it swaps out the current component for the component the response asks for, and pushes state on history.

The mechanics matter more than they look. The request asks for JSON, so the server renders the JSON representation of the posts page, and the component that arrives is not a hardcoded route mapping in the client but whatever the server asked for. The history entry is pushed, so the browser back button works, and because there is no client-side router, the URL is always the truth about where you are.

This is the part of Superglue that most directly answers the no client-side routing claim in the README's opening line. There is a router in the sense that a URL maps to a page, but it is the Rails router, and it is the only one.

Server-side rendering is supported as well, and it is not built into the library. The README points to Humid, a separate thoughtbot project, for that, with the documentation in recipes/server-side-rendering.md. The boundary is sensible, since SSR is an orthogonal concern to the props template mechanism, but it does mean adopting Superglue and then wanting server rendered React means taking a second dependency.

A TypeScript rewrite published as a release candidate

The npm package is @thoughtbot/superglue at version 2.0.0-rc.3, and the repository has no GitHub releases. The default branch is v2, which is the signal that this is the second major line of the project.

The build has moved to modern tooling. The build script runs tsup and then a small script that copies the wrappers, the dev script runs tsup in watch mode, and the test runner is vitest, with separate scripts for watch mode, coverage and a full lint-plus-test target. The lint setup runs eslint, the TypeScript compiler and prettier in parallel, with eslint configured to fail on any warning across js, jsx, ts and tsx in the lib and spec directories.

The exports map shows a package built for more than one consumption model. The root entry exposes types plus an ES module build and a CommonJS build. There are two additional subpath exports, ./deepkit and ./typia, each with its own types and its own module and CommonJS builds, and the typia entry additionally carries a ttsc key pointing at a transformer module.

That last detail is a strong hint about the direction of travel. deepkit and typia are both TypeScript schema and validation libraries, and the presence of a transformer for typia means the library can generate runtime validation from your types, which is how a props template can be checked against the shape the client expects.

The dependency list in the devDependencies confirms the direction too, with the deepkit packages, @rails/actioncable and the typescript toolchain all present. The Ruby side lives in the lib/ directory of the same repository, alongside npm/ for the npm package, plugins/, spec/ for the JavaScript tests, a docs/ folder and a mkdocs.yml for the documentation build, plus typedoc.json and a tsup.config.ts.

The last push was on 2026-09-30, so the code is being worked on, and the licence is MIT with a LICENSE file at the root, so no copyleft obligation comes with using it in a commercial product, which is a licensing note rather than legal advice.

When classic Rails is the wrong assumption

The constraint of the whole design is that you already have a classic Rails application.

Every mechanism described in the README depends on server rendered views. The HTML version of a page comes from an .html.erb file, the JSON version comes from a .json.props template that walks the same view tree, and the controller action responds to both. If your backend is a JSON API in front of a separate service, there is no view tree to walk and PropsTemplate has nothing to dig through.

The second limit is the release state. The npm version is 2.0.0-rc.3, a release candidate, and the repository publishes no GitHub releases, so there is no stable v2 tag to point a lockfile at and no changelog of released versions to diff against. The NEWS.md file at the root is the release history, and reading it is how you would find out what changed between candidates.

The third limit is the JavaScript side of the stack. Superglue is a bridge to React and Redux, and it presumes a redux store with a particular shape, because props_at addresses a path in that store. An application that already uses a different state container, or a component library that expects a different data flow, will find that the graft mechanism is the part that does not fit, not the request layer.

There is also a cultural cost worth naming. The library's appeal is that it removes the API, and that is exactly what makes it unusual. A team that has decided to move to a service oriented architecture, or that wants the frontend to be independently deployable and independently testable, will find that Superglue's central idea is the obstacle rather than the feature. Its own README notes that the frontend is not separable in the way a separate API would be.

The alternative to evaluate against is the obvious one, a JSON API built with Jbuilder and a React frontend with its own router and data layer. The difference in approach is that the API approach moves the page composition to the client, giving you client-side routing and independent deployment, while Superglue keeps composition on the server and gives you the Rails helpers, the flash and cookie auth you already have. Teams that have already standardised on classic Rails with Hotwire and Stimulus may find that Superglue answers a question they have already answered differently, and teams deliberately staying server rendered will find that Superglue lets them use React without leaving Rails.

Editorial conclusion

Adopt Superglue if you have a classic Rails application and want to add interactive React pages without splitting the backend into a separate API service, because the HTML and JSON come from the same view and the same route. Do not adopt it for a greenfield application in 2026, because the repository has no GitHub releases and the current npm version is 2.0.0-rc.3, a release candidate, and because the whole premise is classic Rails. Verify first that your app can serve the same view as HTML and JSON, that the client bundle and the Ruby gem versions are matched, and that the redux store shape PropsTemplate assumes fits how your application already structures its state.

Frequently asked questions

What problem does Superglue solve?

It lets you build rich React and Redux applications in a classic Rails application with no APIs and no client-side routing. The same route serves HTML or JSON depending on the request, and you keep Rails form helpers, UJS, tag helpers, flash and cookie auth.

What is PropsTemplate and how does props_at work?

PropsTemplate is a diggable JSON templating DSL inspired by JBuilder that powers the JSON responses, walking the view tree to a specified node and skipping execution of nodes that do not match the keypath. With props_at set to a path such as data.header.search, the response carries that node and the client immutably grafts it into the same path on the redux store before React re-renders.

How does Superglue handle navigation without a client-side router?

It uses its own UJS attributes inspired by Turbolinks. A link marked with data-sg-visit requests the target path with an accept of application/json, and the client swaps the current component for the one the response asks for and pushes state on history.

Is Superglue server-side rendering supported?

Yes, through Humid, a separate thoughtbot project, with the details in the recipes/server-side-rendering.md document in the documentation. Server-side rendering is not built into Superglue itself, so it means taking a second dependency.

What version of Superglue is published and is it stable?

The npm package @thoughtbot/superglue is at 2.0.0-rc.3, a release candidate, and the repository has no GitHub releases. The default branch is v2, and the last push was on 2026-09-30, with the licence recorded as MIT.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. thoughtbot/superglue on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/thoughtbot-superglue.svg)](https://hysenlabs.com/projects/thoughtbot-superglue)

Was this useful?