StimulusReflex

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

Build reactive applications with the Rails tooling you already know and love. StimulusReflex is designed to work perfectly with server rendered HTML, Russian doll caching, Stimulus, Turbolinks, etc... and strives to live up to the vision outlined in The Rails Doctrine.

Ship projects faster... with smaller teams.

Before you Begin

A great user experience can be created with Rails alone. Tools like UJS remote elements , Stimulus, and Turbolinks are incredibly powerful when combined. Try building your application using these tools before introducing StimulusReflex.

See the Stimulus TodoMVC example application if you are unsure how to do this.

Benefits

StimulusReflex offers 3 primary benefits over the traditional Rails request/response cycle.

  1. All communication happens via web socket - avoids the overhead of traditional HTTP connections

  2. The controller action is invoked directly - skips framework overhead like the middleware chain

  3. DOM diffing is used to update the page - provides faster rendering and less jitter

Setup

yarn add stimulus_reflex
app/javascript/controllers/index.js
import { Application } from 'stimulus'
import { definitionsFromContext } from 'stimulus/webpack-helpers'
import StimulusReflex from 'stimulus_reflex'

const application = Application.start()
const context = require.context('controllers', true, /_controller\.js$/)
application.load(definitionsFromContext(context))
StimulusReflex.initialize(application)
Gemfile
gem "stimulus_reflex"

Quick Start

Here are a few small contrived examples to get you started.

No JavaScript

It's possible to build a reactive application without writing any JavaScript. This requires 2 steps.

  1. Declare the appropriate data attributes in HTML.

  2. Create a server side reflex object with Ruby.

This example will automatically update the page with the latest count whenever the anchor is clicked.

app/views/pages/example.html.erb
<head></head>
  <body>
    <a href="#"
       data-reflex="click->ExampleReflex#increment"
       data-step="1"
       data-count="<%= @count.to_i %>">
      Increment <%= @count.to_i %>
    </a>
  </body>
</html>
app/reflexes/example_reflex.rb
class ExampleReflex < StimulusReflex::Reflex
  def increment
    @count = element.dataset[:count].to_i + element.dataset[:step].to_i
  end
end

Concerns like managing state and template rendering are handled server side. This technique works regardless of how complex the UI becomes. For example, we could render multiple instances of @count in unrelated sections of the page and they will all update.

Do not create server side reflex methods named reflex as this is a reserved word.

Some JavaScript

Real world applications typically warrant setting up finer grained control. This requires 3 steps.

  1. Declare the appropriate data attributes in HTML.

  2. Create a client side StimulusReflex controller with JavaScript.

  3. Create a server side reflex object with Ruby.

This example will automatically update the page with the latest count whenever the anchor is clicked.

app/views/pages/example.html.erb
<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
    // pass the step argument with a value of `1` to the reflex method
    this.stimulate('ExampleReflex#increment', 1);
  }
}
app/reflexes/example_reflex.rb
class ExampleReflex < StimulusReflex::Reflex
  def increment(step = 1)
    # In a typical Rails app the Rails controller would set the value of @count
    # after fetching it from a persistent data store
    # @count = @count.to_i + step

    # To keep this example simple, we use session to store the value
    session[:count] = session[:count].to_i + step
    @count = session[:count]
  end
end

How it Works

Here's what happens whenever a StimulusReflex::Reflex is invoked.

  1. The page that triggered the reflex is re-rerendered.

  2. The re-rendered HTML is sent to the client over the ActionCable socket.

  3. The page is updated via fast DOM diffing courtesy of morphdom.

All instance variables created in the reflex are made available to the Rails controller and view.

The entire body is re-rendered and sent over the socket. Smaller scoped DOM updates may come in a future release.

Example Applications

Last updated