Open-source project
stimulusreflex/stimulus_reflex avatar
stimulusreflex/stimulus_reflex

StimulusReflex: the page changes on the server and arrives over the cable

Build reactive applications with the Rails tooling you already know and love.

2,337 stars172 forksRubyMIT

At a glance

What is it?
StimulusReflex turns a server-side Ruby call into HTML that the server renders and pushes down an Action Cable socket, so a Rails app can react without a separate frontend framework. It ships as a gem and an npm package that have to be cut together by hand.
Who is it for?
StimulusReflex fits a team already inside Rails that wants reactive pages without a second frontend build, and it fits badly if you need offline behaviour or client-side state, neither of which the README describes. Teams running a separate single page front end get nothing from it either, because the design assumes ERB is still the view layer.
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 5 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • stimulusreflex/stimulus_reflex2,337 ★ · -1
Star history
Repository2026-10-142026-10-21
stimulusreflex/stimulus_reflex2.3k2.3k

-1 Stars in the last 7 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The reflex comes back as a rendered page fragment

StimulusReflex targets one specific gap: a Rails application that wants reactive behaviour without adopting a frontend framework and the build pipeline that arrives with it. The unit of work is a reflex, and a reflex is server-side work whose result is HTML the application renders and sends to the browser. The README puts its speed claim on the transport rather than on the rendering: server-rendered HTML, delivered in milliseconds over Websockets. CableReady is named as the component doing the pushing, described in one line as the project's secret power-move, and `@rails/actioncable` heads the npm dependency list, so the channel is the one Rails already runs.

Templates come from the Rails view layer instead of from JavaScript. ERB templates and partials are the first-class path, with ViewComponent support called out explicitly, so a component rendered on the server can back a reactive surface without a second component system appearing in the browser. Waiting also happens on Rails terms: Russian doll caching sits alongside ActiveJob in the feature list, which puts expensive work in a Rails queue and a fragment cache rather than in a client-side spinner loop.

On the browser side the client pairs StimulusJS with a page navigation layer, and the README names both Turbolinks and Turbo Drive, so the library spans the pre-Turbo and Turbo generations of Rails rather than choosing one. Two consequences follow. Application state does not live in the browser, so there is no client-side store to synchronise and no API contract to design against. Every interaction that changes the page also costs a round trip to your own servers. That trade is fair for a CRUD screen with modest traffic and awkward for anything that must stay responsive while the server is slow.

Two packages that must agree on one version number

StimulusReflex is distributed twice from one repository: a Ruby gem and an npm package. The gem goes into a Rails app through Bundler, the package into the browser build, and both manifests currently read version 3.5.5. Nothing in the tooling keeps them in step automatically, and the publishing checklist at the foot of the README shows the manual work involved. It opens with an ordering rule: CableReady ships first, because `cable_ready` is a declared dependency of both `stimulus_reflex.gemspec` and `package.json`. Step two of that checklist asks for the dependency version to be updated in both files, and step four asks for a bump at `lib/stimulus_reflex/version.rb`.

The same numbered list contains an inconsistency worth noticing. The Ruby step spells a prerelease suffix as `.preN`, while the yarn step spells the equivalent suffix as `-preN`, so one list gives the same idea two spellings for two package managers. Anyone cutting one by hand has to know which form belongs in which file, and nothing in the repository automates the choice.

The published cadence is irregular rather than absent. The last push was on 2026-09-13, and the most recent tagged versions are v3.5.5 on 2026-07-28, v3.5.4 on 2025-05-22 and v3.5.3 on 2024-12-15, which leaves gaps of months between builds. Building and publishing runs through a short command list:

sh
bundle exec rake build
yarn build
bin/standardize
bundle exec rake release
yarn publish --no-git-tag-version

After the yarn step the maintainer commits and pushes twice, once before the build and once after the new version has been typed at the yarn prompt, then finishes the sequence by hand on GitHub. Two source trees, one shared number, and a human holding it together.

Three ways to attach the JavaScript client

The README splits installation into a Ruby half and a browser half, and it states the limit of what it hands you plainly: CLI and manual setup procedures are fully detailed in the official docs, at https://docs.stimulusreflex.com/hello-world/setup.html. What follows is the entire command surface printed in the README itself. The gem goes in through Bundler:

sh
bundle add stimulus_reflex

The JavaScript client has three install shapes, and the right one depends on how the application serves assets. Under ESBuild or Webpacker it is an npm add:

sh
yarn add stimulus_reflex

Importmap-based applications pin the file by hand in `config/importmap.rb`:

ruby
# config/importmap.rb

# ...

pin 'stimulus_reflex', to: 'stimulus_reflex.js', preload: true

Sprockets applications load the UMD build from the layout, with a reload hint attached to the tag so a deploy is picked up:

html+erb
<!-- app/views/layouts/application.html.erb -->

<%= javascript_include_tag "stimulus_reflex.umd.js", "data-turbo-track": "reload" %>

After any of these you should see a resolvable `stimulus_reflex.js` or `stimulus_reflex.umd.js` in the asset pipeline, plus a gem that can find the cable. Nothing else in the README says what a working setup looks like next.

What the npm manifest ships is a build artifact

The npm package publishes from `dist/`. In `package.json`, `main`, `module`, `browser` and `import` all point at `./dist/stimulus_reflex.js`, while `unpkg` and the legacy umd entry point at `./dist/stimulus_reflex.umd.js`, the same file the Sprockets tag loads. The `files` array lists `dist/*` and `javascript/*`, so what reaches the registry is a built bundle alongside sources, never the JavaScript as it sits in `javascript/`.

The build itself is Rollup. `yarn build` runs `yarn rollup -c` against `rollup.config.mjs`, and `build:watch` re-runs the same command on change for local development. Pinning the client to Stimulus is handled by a peer dependency rather than a hard dependency: `@hotwired/stimulus` must satisfy `>= 3.0`. An application still on Stimulus 2 cannot take the package without a framework upgrade first, which makes this the sharpest install-time constraint in the whole setup.

Documentation lives in the repository as a VitePress site, with `docs:dev` starting `vitepress dev docs`, `docs:build` producing the static output, and `docs:preview` serving the built copy. Ruby and JavaScript source share one version number and one licence: both manifests declare the MIT licence, and the text itself is kept in LICENSE.txt at the repository root, so there is no separate licence to read for the browser half.

The JavaScript lint never reads the Ruby tree

A close look at the scripts reveals two independent test and style stacks. JavaScript tests run under web-test-runner: `yarn test` executes `web-test-runner javascript/test/**/*.test.js`, driven by `web-test-runner.config.mjs` at the repository root, and `lint` runs the format check over `./javascript/**/*.js` and `rollup.config.mjs` through Prettier-Standard. Nothing in that path touches `lib/` or `test/`, which is where the Ruby gem and its own tests live.

Ruby style is handled outside npm entirely, through Standard, with Prettier-Standard covering the JavaScript side and `./bin/standardize` run before opening a pull request. The top-level entries explain the split. `Gemfile`, `Gemfile.lock`, `Rakefile`, `stimulus_reflex.gemspec`, `app/`, `lib/` and `test/` sit on one side; `javascript/`, `dist/`, `rollup.config.mjs`, `web-test-runner.config.mjs` and `yarn.lock` on the other, with `.standard.yml` and `.prettierignore` keeping the two style systems apart. The README does not say what `./bin/standardize` executes internally, so a contributor cannot tell from the README whether the JavaScript formatter runs in that script too or only the Ruby one. Contributors are also pointed at a wiki page for editor configuration, and everyone touching code is expected to follow the Code of Conduct file.

That arrangement is cheap for a small project and awkward for a contributor who arrives from either side alone: the npm test command will never tell you whether your Ruby change broke anything, and the Rake tasks will never tell you whether your client change did.

Where this design is the wrong tool

Putting a server round trip in front of every page change is a good trade for admin screens and a bad one for anything that has to survive a weak connection. Offline behaviour is absent from the documentation: there is no client-side state layer, no queue of pending changes, and no documented handling for a cable that drops in the middle of an interaction. A project with a real offline story, heavy client-side computation, or a mobile-first interface has to build that layer itself, which means owning state the library never intended to hold.

The support path is the second constraint. The Discord is the recommended first stop, GitHub issues come second, and Stack Overflow support is declined outright in one sentence. A reproduction template is offered at leastbad/stimulus_reflex_harness for anyone filing a bug, and the ask is a git repository with the relevant code rather than a fragment, which is a reasonable request and a real amount of work before an issue is worth writing. With a community room of over 2000 members and a dedicated #newcomers channel, most questions are answered in chat, where they stay searchable only for the people present.

The demo list is candid about the same limit. BeastMode shows faceted search with filtering, sorting and pagination, StimulusReflex Patterns hosts single-file applications on Glitch, and Boxdrop is described as a concept demo modelled on Dropbox. Those are illustrations of what is possible, not reference deployments, so a team should not read them as an estimate of the work involved in production.

The README stops where the setup page begins

Everything above is the easy half of this project. The detailed guides are hosted rather than vendored: the setup walkthrough on the docs site, separate documentation for CableReady at cableready.stimulusreflex.com, and a condensed cheat sheet on devhints.io/stimulus-reflex. The repository itself never explains how a reflex attaches to a controller action, so the distance between an installed gem and a working page change is covered somewhere else entirely.

Topic tags describe the intended neighbourhood rather than a documented API: `actioncable`, `cableready`, `hacktoberfest`, `hotwire`, `server-side-rendering`, `turbo` and `turbolinks` alongside `stimulus` and `rails`. The presence of `hacktoberfest` says something about how outside contributions are expected to arrive, and the presence of both `turbolinks` and `turbo` confirms that older Rails applications are still in scope.

The gap worth flagging is upgrade guidance. Nothing in the repository documents how to move an application forward across the version numbers quoted above, or what a peer dependency bump of `@hotwired/stimulus` costs an app that has been left behind on an older Stimulus. What the project offers in place of written migration advice is social: Discord first, a wiki page for editor configuration, and a Code of Conduct file that everyone touching the code is expected to follow.

Editorial conclusion

StimulusReflex fits a team already inside Rails that wants reactive pages without a second frontend build, and it fits badly if you need offline behaviour or client-side state, neither of which the README describes. Teams running a separate single page front end get nothing from it either, because the design assumes ERB is still the view layer. Before adopting it, read the setup page at docs.stimulusreflex.com/hello-world/setup.html, confirm your Stimulus version satisfies the >= 3.0 peer dependency, and watch how your Action Cable connection recovers before building on reflexes.

Frequently asked questions

How do I install StimulusReflex in a Rails application?

Add the gem with `bundle add stimulus_reflex`, then attach the client the way your asset pipeline expects: `yarn add stimulus_reflex` under ESBuild or Webpacker, a pin line in `config/importmap.rb` for importmaps, or `<%= javascript_include_tag "stimulus_reflex.umd.js", "data-turbo-track": "reload" %>` in the layout under Sprockets. The full CLI and manual setup steps are documented at https://docs.stimulusreflex.com/hello-world/setup.html.

Which Stimulus version does StimulusReflex require?

The npm package declares `@hotwired/stimulus` as a peer dependency of `>= 3.0`, so an application still on Stimulus 2 has to upgrade before installing the client. The package also depends on `@rails/actioncable`, which is what carries the rendered HTML.

Does StimulusReflex provide an API layer like a single page application framework?

No. The README describes server-rendered HTML delivered over Websockets, using ERB templates, partials and ViewComponent, with Russian doll caching and ActiveJob doing the waiting. There is no documented client-side state store, which means each page change depends on a round trip to your own server.

How is a new version of StimulusReflex published?

The gem and the npm package are cut together by hand. The README's publishing checklist ships CableReady first, updates `cable_ready` in `stimulus_reflex.gemspec` and `package.json`, bumps `lib/stimulus_reflex/version.rb`, then runs `bundle exec rake build` with `yarn build` and `yarn publish --no-git-tag-version`, finishing with a manual artefact on GitHub.

Where should I ask for help with StimulusReflex?

The Discord is the recommended first stop, with a #newcomers channel for new arrivals and a request that questions come with a git repository to reproduce from. GitHub issues come second, and the reproduction template at leastbad/stimulus_reflex_harness is offered for that purpose. Stack Overflow support is declined outright.

Does StimulusReflex work with Turbolinks as well as Turbo Drive?

The README pairs StimulusJS with both Turbolinks and Turbo Drive, and the Sprockets install snippet attaches `data-turbo-track` to the script tag so a deploy is picked up. Applications on either navigation layer are therefore in scope.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. stimulusreflex/stimulus_reflex 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/stimulusreflex-stimulus-reflex.svg)](https://hysenlabs.com/projects/stimulusreflex-stimulus-reflex)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/stimulusreflex-stimulus-reflex.svg)](https://hysenlabs.com/projects/stimulusreflex-stimulus-reflex)