Ruact::Controller
Ruact::Controller is an ActiveSupport::Concern that adds RSC rendering capability to any Rails controller.
Setup
Include it in ApplicationController:
class ApplicationController < ActionController::Base
include Ruact::Controller
endThat is the only setup required. All controller actions that have a corresponding .html.erb template will automatically serve RSC payloads.
Automatic rendering
Once the concern is included, the default_render hook activates RSC rendering automatically when:
- A matching
.html.erbtemplate exists for the current action - The request is either an HTML request (
Accept: text/html) or an RSC request (Accept: text/x-component)
For all other formats (JSON, XML, etc.), the default Rails rendering is used unchanged.
ruact_render
Renders the RSC view for the current action.
def ruact_render(template: nil, locals: {})Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
template | String, nil | nil | Logical template name (e.g. "posts/custom"). When nil, uses the current action's default template. |
locals | Hash | {} | Local variables to pass to the template. |
Behaviour
- RSC request (
Accept: text/x-componentorRuact-Request: 1header): responds with atext/x-componentFlight payload. Streaming is used whenActionController::Liveis included. - HTML request: responds with an HTML shell containing the Flight payload inline in a
<script>block.
Examples
# Use the action's default template (most common)
def index
@posts = Post.all
ruact_render
end
# Render a different template
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post
else
ruact_render template: "posts/new"
end
end
# Pass locals
def show
@post = Post.find(params[:id])
ruact_render locals: { highlight: params[:highlight] }
endredirect_to (overridden for RSC)
The concern overrides Rails' redirect_to for RSC requests. Instead of issuing a 302 HTTP redirect, it emits a Flight redirect row:
0:{"redirectUrl":"/posts/1","redirectType":"push"}The client-side router intercepts this row and navigates without an extra HTTP round-trip.
Behaviour:
- RSC request + same-origin URL: emits Flight redirect row
- RSC request + cross-origin URL: falls back to standard
302redirect - Non-RSC request: falls back to standard Rails
redirect_to
def create
@post = Post.create!(post_params)
redirect_to post_path(@post) # ← handled by RSC client when called from JS navigation
endStreaming
To enable streaming (progressive render of Suspense boundaries), include ActionController::Live:
class PostsController < ApplicationController
include ActionController::Live
include Ruact::Controller
def index
@posts = Post.all
ruact_render
end
endWith ActionController::Live, Suspense fallbacks are flushed immediately and the deferred content is streamed as it resolves. Without it, the full Flight payload is buffered and sent at once.
Detecting RSC requests
The concern exposes a private helper you can use in before_actions or other callbacks:
before_action :require_authentication, unless: :ruact_request?An RSC request is one that includes Accept: text/x-component or the header Ruact-Request: 1.