Skip
Arish's avatar

37. Rendering Responses


Rendering Responses

Controllers are responsible for creating responses to send back to the browser. Rails provides many ways to render content.

Render vs Redirect

ruby
1class ArticlesController < ApplicationController
2  def create
3    @article = Article.new(article_params)
4    
5    if @article.save
6      # Redirect: sends 302, browser makes new request
7      redirect_to @article
8    else
9      # Render: returns response immediately, no new request
10      render :new, status: :unprocessable_entity
11    end
12  end
13end

Rendering Templates

Default Rendering

ruby
1class ArticlesController < ApplicationController
2  def show
3    @article = Article.find(params[:id])
4    # Automatically renders app/views/articles/show.html.erb
5  end
6end

Explicit Rendering

ruby
1class ArticlesController < ApplicationController
2  def show
3    @article = Article.find(params[:id])
4    
5    # Render same template explicitly
6    render :show
7    
8    # Render different template
9    render :special_show
10    
11    # Render template from another controller
12    render "products/show"
13    
14    # Render with absolute path
15    render file: "/path/to/template"
16  end
17end

Render Options

ruby
1# With status code
2render :new, status: :unprocessable_entity  # 422
3render :error, status: :not_found           # 404
4render :error, status: 500                  # Numeric codes work too
5
6# Without layout
7render :show, layout: false
8
9# With different layout
10render :show, layout: "admin"
11
12# With locals
13render :show, locals: { article: @article, author: @author }
14
15# Set content type
16render :show, content_type: "text/plain"

Rendering Non-HTML

JSON

ruby
1def show
2  @article = Article.find(params[:id])
3  
4  # Simple JSON
5  render json: @article
6  
7  # With options
8  render json: @article, only: [:id, :title, :body]
9  
10  # With includes
11  render json: @article, include: :comments
12  
13  # Custom structure
14  render json: {
15    article: @article,
16    meta: { generated_at: Time.current }
17  }
18  
19  # With status
20  render json: { error: "Not found" }, status: :not_found
21end

Other Formats

ruby
1def show
2  @article = Article.find(params[:id])
3  
4  # Plain text
5  render plain: "Hello World"
6  
7  # HTML string
8  render html: "<h1>Hello</h1>".html_safe
9  
10  # XML
11  render xml: @article
12  
13  # Raw body
14  render body: "Raw content"
15  
16  # Nothing (empty response)
17  head :ok
18  head :no_content
19  head :created
20end

Respond To Multiple Formats

ruby
1class ArticlesController < ApplicationController
2  def index
3    @articles = Article.all
4    
5    respond_to do |format|
6      format.html # renders index.html.erb
7      format.json { render json: @articles }
8      format.xml { render xml: @articles }
9      format.csv { render plain: @articles.to_csv }
10      format.pdf { send_pdf }
11    end
12  end
13  
14  private
15  
16  def send_pdf
17    pdf = ArticlesPdf.new(@articles)
18    send_data pdf.render,
19              filename: "articles.pdf",
20              type: "application/pdf",
21              disposition: "inline"
22  end
23end

Rendering Partials

ruby
1def show
2  @article = Article.find(params[:id])
3  
4  # Render partial as response
5  render partial: "article"
6  
7  # With locals
8  render partial: "article", locals: { article: @article }
9  
10  # Collection
11  render partial: "article", collection: @articles
12  
13  # With layout for partial
14  render partial: "article", layout: "card"
15end

Streaming Responses

ruby
1class ReportsController < ApplicationController
2  include ActionController::Live
3  
4  def download
5    response.headers["Content-Type"] = "text/csv"
6    response.headers["Content-Disposition"] = 'attachment; filename="report.csv"'
7    
8    response.stream.write "id,name,email\n"
9    
10    User.find_each do |user|
11      response.stream.write "#{user.id},#{user.name},#{user.email}\n"
12    end
13  ensure
14    response.stream.close
15  end
16end

Sending Files

ruby
1class DownloadsController < ApplicationController
2  def show
3    # Send existing file
4    send_file Rails.root.join("files", "document.pdf"),
5              filename: "document.pdf",
6              type: "application/pdf",
7              disposition: "attachment"  # or "inline"
8    
9    # Send generated data
10    send_data generate_csv,
11              filename: "report.csv",
12              type: "text/csv"
13  end
14  
15  private
16  
17  def generate_csv
18    CSV.generate do |csv|
19      csv << ["ID", "Name", "Email"]
20      User.find_each do |user|
21        csv << [user.id, user.name, user.email]
22      end
23    end
24  end
25end

Redirects

ruby
1class ArticlesController < ApplicationController
2  def create
3    @article = Article.new(article_params)
4    
5    if @article.save
6      # Redirect to show action
7      redirect_to @article
8      
9      # Or explicit path
10      redirect_to article_path(@article)
11      
12      # With flash message
13      redirect_to @article, notice: "Created!"
14      redirect_to @article, alert: "Warning!"
15      
16      # With custom flash
17      redirect_to @article, flash: { success: "Great job!" }
18      
19      # External URL
20      redirect_to "https://example.com"
21      
22      # Back to previous page
23      redirect_back(fallback_location: root_path)
24      
25      # With status
26      redirect_to @article, status: :see_other  # 303
27    end
28  end
29end

Flash Messages

ruby
1class ArticlesController < ApplicationController
2  def create
3    @article = Article.new(article_params)
4    
5    if @article.save
6      flash[:notice] = "Article created!"
7      redirect_to @article
8    else
9      flash.now[:alert] = "Please fix errors"
10      render :new
11    end
12  end
13  
14  def update
15    # Keep flash for one more request
16    flash.keep(:notice)
17    redirect_to articles_path
18  end
19end
erb
1<!-- In layout -->
2<% flash.each do |type, message| %>
3  <div class="alert alert-<%= type %>">
4    <%= message %>
5  </div>
6<% end %>

Head (Status Only)

ruby
1class ArticlesController < ApplicationController
2  def check
3    if Article.exists?(params[:id])
4      head :ok
5    else
6      head :not_found
7    end
8  end
9  
10  def destroy
11    Article.find(params[:id]).destroy
12    head :no_content  # 204
13  end
14end

Proper rendering makes your controllers communicate effectively with clients!