November 11, 2020•
Custom Error Pages in Ruby on Rails
Ruby on Rails ships with default error pages for more popular requests you might encounter including
422. Each request has an associated static
HTML page that lives inside every new Ruby on Rails app's
There may come a time where those static pages are a bit cumbersome to work with or you want to add a little more flair to yours using assets that already exist in your app. The public folder by design lives outside of the bound of what Rails and serve so you will see that each
html page has embedded styles and content.
This works for basic error pages but in my own case I wanted to leverage Tailwind CSS that I was already using inside my application. We can do this in a few steps but that means tweaking some configuration and telling your app how to handle errors in a new custom way.
Start with a controller
Generating a new
ErrorsController that inherits from
ApplicationController is a good first step in creating your custom error pages. With this controller we can render specific statuses that will ultimately render the corresponding views instead of those
html templates in the
$ rails g controller errors not_found internal_server_error
The command above generates the controller and views we need. It also creates a helper file, basic routing, and some test files. Note the new methods
internal_server_error in the controller.
Within those we can tell our app what to render as a response. Each method maps to a different response.
# app/controllers/errors_controller.rb class ErrorsController < ApplicationController def not_found render status: 404 end def internal_server_error render status: 500 end end
While our controller is set up for success we still need to add the appropriate routing to handle our new error pages.
# config/routes.rb Rails.application.routes.draw do ... match "/404", to: "errors#not_found", via: :all match "/500", to: "errors#internal_server_error", via: :all ... end
match methods we can pass in a relative path to the request we passed down from the controller. If that path is matched in the request cycle we instruct our app to target the methods on our
ErrorController class. The
via: :all essentially means match all requests.
Adjusting our configuration
Even with our new controller and routes set up we still need to turn off the default way Rails will handle errors. You can append the following to your main
... module YourAppName class Application < Rails::Application config.load_defaults 6.0 # I'm using Rails 6 at the time of this article config.exceptions_app = self.routes # Add this line end end
The main line we need to add is
config.exceptions_app = self.routes. This essentially bypasses the internal error logic and tells the application to head to
config/routes.rb for further instruction.
Testing things out locally
While the code we have so far should work in the production environment you might want to test it out locally. To do this you need to disable a configuration in
# config/environments/development.rb Rails.application.configure do # Show full error reports. config.consider_all_requests_local = false end
Look for the line
config.consider_all_requests_local = true and swap that to
false temporarily. At this point you should restart your server (
rails server) and proceed to some unknown URL on your app. I went to
localhost:3000/asdf for example.
You should now see the corresponding error view templates in your browser instead of those found in the
public folder. We can now use our app's styling (Tailwind CSS in my case) to edit and adjust the views within
Now that you have custom error templates you can proceed to style those to match the design of your application in a much more efficient way. This took a little set up and can be extended much further but I hope you found it useful.