Build reactive Single Page Applications (SPAs) with Rails and Stimulus
This project supports building reactive applications with the Rails tooling you already know and love. It's designed to work perfectly with server rendered HTML, Russian doll caching, Stimulus, Turbolinks, etc...
No need for a complex front-end framework. No need to grow your team or duplicate your efforts.
Inspired by Phoenix LiveView. 🙌
StimulusReflex provides functionality similar to what can already be achieved with Rails by combining UJS remote elements , Stimulus, and Turbolinks. Consider building with standard Rails tooling before introducing StimulusReflex. Check out the Stimulus TodoMVC example if you are unsure how to accomplish this.
StimulusReflex offers 3 primary benefits over the traditional Rails HTTP request/response cycle.
- Communication happens on the ActionCable web socket - saves time by avoiding the overhead of establishishing traditional HTTP connections
- The controller action is invoked directly - skips framework overhead such as the middleware chain, etc...
- DOM diffing is used to update the page - provides faster rendering and less jitter
- Render a standard Rails view template
- Use Stimulus and ActionCable to invoke a method on the server
- Watch the page automatically render updates via fast DOM diffing
- That's it...
Yes, it really is that simple. There are no hidden gotchas.
yarn add stimulus_reflex
gem "stimulus_reflex"
<head></head>
<body>
<a href="#" data-controller="example" data-action="click->example#increment">
Increment <%= @count.to_i %>
</a>
</body>
</html>
import { Controller } from "stimulus"
import StimulusReflex from "stimulus_reflex"
export default class extends Controller {
connect() {
StimulusReflex.register(this);
}
increment() {
// trigger a server-side reflex and a client-side page update
this.stimulate('ExampleReflex#increment', 1);
}
}
class ExampleReflex < StimulusReflex::Reflex
def increment(step = 1)
@count = @count.to_i + step
end
end
The following happens after the StimulusReflex::Reflex
method call finishes.
- The page that triggered the reflex is re-rerendered. Instance variables created in the reflex are available to both the controller and view templates.
- The re-rendered HTML is sent to the client over the ActionCable socket.
- The page is updated via fast DOM diffing courtesy of morphdom. While future versions of StimulusReflex might support more granular updates, today the entire body is re-rendered and sent over the socket.
StimulusReflex will use the ActionCable defaults of window.App
and App.cable
if they exist.
If these defaults do not exist, StimulusReflex will establish a new socket connection.
ActionCable emits verbose log messages. Disabling ActionCable logs may improve performance.
# config/application.rb
ActionCable.server.config.logger = Logger.new(nil)
You may find the need to restrict notifications to a specific room.
This can be accomplished by setting the data-room
attribute on the StimulusController element.
<a href="#" data-controller="example" data-action="click->example#increment" data-room="12345">
An attempt is made to reduce repaint/reflow jitter when users trigger lots of updates.
You can control how long to wait (think debounce) prior to updating the page.
Simply set the renderDelay
(milliseconds) option when registering the controller.
export default class extends Controller {
connect() {
StimulusReflex.register(this, {renderDelay: 200});
}
}
The default value is 25
.
Building apps with StimulusReflex should evoke your memories of the original Rails demo video.
Look at all the things I'm not doing. -DHH
This project uses Standard
and Prettier to minimize bike shedding related to code formatting.
Please run ./bin/standardize
prior submitting pull requests.