How to Create Custom Error Pages in Rails.

Published on October 03, 2024
Written by Victor Cobos

Error pages are a critical, but often overlooked, part of any web application. When something goes wrong — whether a user lands on a broken link or the server encounters an issue — the way you handle these moments can make a lasting impression. Instead of leaving users to face a generic or confusing error message, creating custom error pages allows you to maintain your brand's voice, guide users back on track, and even turn a frustrating experience into a more positive one.

In this guide, I'll show you how to create custom error pages in a Ruby on Rails application. We'll walk through setting up custom pages for common HTTP errors, such as 404 (Not Found) and 500 (Internal Server Error), and cover how to handle these errors dynamically. By the end, you'll have a polished set of error pages that improve both the user experience and the overall feel of your application.

TL;DR

If you’re looking for a quick solution, you can set up custom error pages in your Rails app with a single command. Just run:

rails app:template LOCATION="https://railsbytes.com/script/VQLs5w"

This template will automatically take care of all the steps outlined in this guide, saving you time and effort while still giving you a personalized error page setup. It’s hosted on RailsBytes, a great resource for sharing and using reusable Rails templates to streamline development tasks.

1. Removing the Default Static Error Pages

The first step is to get rid of the default static error pages that Rails provides. These files (404.html, 422.html, and 500.html) are located in the public/ directory. Removing them will ensure that Rails looks to your custom error handling setup instead of falling back to the default pages. This clears the way for your personalized error pages.

rm public/*.html

2. Generating a New Controller for Error Pages

Next, you'll create a dedicated controller to manage your custom error pages. This gives you more control over how each error is handled and allows you to render dynamic views. Run the Rails generator to create an ErrorsController:

rails generate controller Errors show

3. Configuring the ErrorsController

Now, configure the ErrorsController to handle errors with a single action. In the show method, you'll retrieve the status code dynamically from the request and render the appropriate response based on the request format. Here's an example of how this might look:

class ErrorsController < ApplicationController
  VALID_STATUS_CODES = %w[400 404 406 422 500].freeze
  def show
    status_code = VALID_STATUS_CODES.include?(params[:code]) ? params[:code] : 500
    respond_to do |format|
      format.html { render status: status_code }
      format.any { head status_code }
    end
  end
end

This approach allows you to manage all error responses from a single controller action, reducing redundancy and keeping things clean. You'll only need one view to handle all errors.

4. Create the Error View

With the ErrorsController set up, it's time to create a single view that handles all your error pages. You can style this view to match your site's design, ensuring the error page fits seamlessly with the rest of your application. This view will be located at app/views/errors/show.html.erb.

Here's an example of a simple, customizable error page:

<h1><%= response.code %></h1>
<p>
  <% case response.code.to_i %>
  <% when 400 %>
    Bad Request.
  <% when 404 %>
    Not Found.
  <% when 406 %>
    Not Acceptable.
  <% when 422 %>
    Unprocessable Entity.
  <% when 500 %>
    Internal Server Error.
  <% else %>
    An unexpected error occurred.
  <% end %>
</p>
<%= link_to "Go back home", root_path %>

5. Adding Error Routes

To ensure that Rails routes all error requests to your custom ErrorsController, you'll need to add the appropriate routes to config/routes.rb.
By constraining the route to common HTTP error codes such as 400, 404, 406, 422 and 500, you can handle those errors dynamically. Here's the route:

Rails.application.routes.draw do
  # ... other routes ...

  match "/:code", 
  to: "errors#show", 
  via: :all, 
  constraints: { 
    code: Regexp.new(
      ErrorsController::VALID_STATUS_CODES.join("|")
    ) 
  }  
end

This approach ensures that only the specified error codes are routed to the ErrorsController for handling, giving you control over how these errors are displayed.

6. Configuring Rails to Use Custom Error Pages

The final step is to tell Rails to use your custom error pages instead of the default static ones. You can achieve this by adding a configuration to your config/application.rb file. This line ensures that Rails routes all exceptions through your application's routes, allowing the ErrorsController to handle them:

config.exceptions_app = routes

This simple configuration enables your application to dynamically render error pages based on the routes you've defined, ensuring that users always see your custom-designed error pages.

7. Writing Tests for Error Pages

To ensure that your custom error handling works correctly, it's important to write tests. In test/controllers/errors_controller_test.rb, you can create tests that check if your ErrorsController properly handles the specified error codes and renders the correct responses.
Here's an example of the test setup:

require "test_helper"

class ErrorsControllerTest < ActionDispatch::IntegrationTest
  VALID_STATUS_CODES = ErrorsController::VALID_STATUS_CODES

  VALID_STATUS_CODES.each do |status_code|
    test "should render #{status_code} page" do
      get "/#{status_code}"
      assert_response status_code.to_i
      assert_select "h1", status_code
    end
  end

  test "should return status code for non-html formats" do
    get "/404", as: :json
    assert_response :not_found
  end
end

These tests ensure that your error pages render correctly for both HTML and non-HTML formats, verifying that the appropriate status codes are returned and the content is as expected.

Bonus: Viewing Error Pages in Development

By default, Rails shows detailed error messages in development mode, which prevents you from seeing your custom error pages. To test these pages locally, you'll need to change the consider_all_requests_local setting in your config/environments/development.rb file. Set it to false to make Rails render your custom error pages:

# config/environments/development.rb
config.consider_all_requests_local = false

This change ensures that your custom error pages will be shown even when running the app locally, allowing you to test and tweak them as needed.

Exciting news: With Rails 8 on the horizon, there's a new error page design coming, thanks to this pull request by @seanmitchell. The new design will enhance the default error pages, making them more visually appealing and user-friendly! 😍

Conclusion

Errors are inevitable in any application, but they don't have to be a frustrating dead end for your users. By creating custom error pages, you can turn these moments into an opportunity to reinforce your brand and provide a positive experience, even when things go wrong.
Instead of relying on generic, uninspired error screens, use these pages to guide users, offer helpful links, or even inject a little creativity and humor. Since errors will happen, why not make the most of them? With the steps outlined in this guide, you can ensure that every aspect of your application, even the unexpected errors, delivers a thoughtful and polished experience.

Subscribe to get future articles via the RSS feed .