Have you ever wanted to create static pages in Rails? These are pages that don’t necessarily contain any dynamic info or pull from the database and don’t require an entire controller.
While some pre-built gems allow this to be done fairly quickly, it’s also good practice to roll your own. Let’s see how we can create some static pages in Rails using our own application.
Table of Contents
- Rails Static Pages Setup
- Generate a Pages Controller
- Create a Route
- Create a Static Page
- A Static Home Page
- Handling Not Found errors in Rails Static Pages
- Rails Static Pages, Slightly Enhanced
Contents
Rails Static Pages Setup
For the rest of this tutorial, I’m going to be using a Rails 4.2 application. These instructions should work in other versions of Rails but I’ve only tested against this version.
If you’d like to follow along, install the latest version of Ruby on Rails and skip ahead. If you don’t have Ruby on Rails installed, there are some great tutorials for OS X, Windows, and Ubuntu over on Treehouse that will get you up and running.
Generate a Pages Controller
Since we’re doing Rails static pages only, we don’t need to generate any models to interface with a database. You can either create a controller yourself or use Rails to generate one.
Either way, we’ll skip creating the CSS and JavaScript that go along with it. If you’d like to use Rails to generate a controller, run this command:
bin/rails generate controller pages --skip-assets
Alternatively, just create it yourself using the command line:
touch app/controllers/pages_controller.rb
And place the following in the app/controllers/pages_controller.rb
file:
class PagesController < ApplicationController
def show
end
end
That will set up an empty show
action in our controller. The show
action in the pages controller will be what we use to render our static page.
Now that we have a show action, we need to make it do something. We’re going to tell the show
action to render a page template that is the same name as a parameter that is passed in.
We’re going to call the parameter page
. We’re going to assume that the static page lives in the app/views/pages
directory and has an extension of html.erb
. Rails looks for that extension to render automatically, so we can leave it off.
Our show action will now look like this:
class PagesController < ApplicationController
def show
render template: "pages/#{params[:page]}"
end
end
With the show
action all set up, we can move on to the routes.
Create a Route
The route is really easy and is just a single entry in our routes.rb
file:
Rails.application.routes.draw do
get "/pages/:page" => "pages#show"
end
This will send every url that matches /pages/
to our pages controller we created above. Any of the following would now work:
- /pages/about
- /pages/home
- /pages/features
Based on the controller we created above, we now have to place our static pages in the app/views/pages
directory and they will show up.
Create a Static Page
Creating static pages in Rails is very easy with the above setup. Just create the file in your terminal (or text editor):
touch app/views/pages/about.html.erb
Now fill it with some content. I’ve chosen to use some cupcake ipsum text because cupcakes are delicious. Save the file and head over to http://localhost:3000/pages/about
and you should see the following:
It works! Awesome! This is a great start.
A Static Home Page
It may be a requirement for your application to have a static home page with no dynamic content. That’s simple to add to our current setup. First, we’ll need to create the home page.
Place the following in app/views/pages/home.html.erb
.
<h2>Home Page</h2>
<p>This would be a static home page in a Rails application.</p>
This is just a place holder. Your real content can go in there later. Next, we’ll tell Rails to look for that as the default root page.
We do that by passing in the page
parameter to the root
route directive. Now we just have to add the following to config/routes.rb
, just before the closing end
keyword:
root "pages#show", page: "home"
Now when you go to http://localhost:3000
you should see the following:
If you see the following screen, you may have named it something other than app/views/pages/home.html.erb
:
Double check that for errors and reload the page.
Handling Not Found Errors in Rails Static Pages
We have basic static page rendering set up but we haven’t planned for what happens when someone hits a page that doesn’t exist.
We need to tell the visitor that the page is not found, and we’d be good net citizens to return a 404 status code as well. We can handle all of this in the pages controller.
In order to do all of that, we’ll create a method called valid_page?
that checks to make sure the page actually exists in our Rails application where we expect it to.
The method exist?
on the File
class will return true or false if the file actually exists in that spot in the file system. We’ll pass that method an instance of the PathName
class which has the full path of the requested template file on disk. The whole method will return true
or false
if the file exists.
From there, all we need to do is render a not found page if the file doesn’t exist. A default Rails application will have a 404 page in the public
directory, so we can just use that. We’ll also send down a 404
status with the :not_found
symbol. Here’s what the pages controller now looks like:
class PagesController < ApplicationController
def show
if valid_page?
render template: "pages/#{params[:page]}"
else
render file: "public/404.html", status: :not_found
end
end
private
def valid_page?
File.exist?(Pathname.new(Rails.root + "app/views/pages/#{params[:page]}.html.erb"))
end
end
Now if we go a nonexistent page, we should see the following:
Rails Static Pages, Slightly Enhanced
The Rails static pages are now all set up but we’ve decided to throw some marketing pages in to show off some of our application’s features.
Marketing wants us to put these at /pages/features/one
, /pages/features/two
, and so on. This is easy enough and only requires a small change in our code. Open up the routes and change this:
get "/pages/:page" => "pages#show"
To this:
get "/pages/*page" => "pages#show"
That small change will pass the page
parameter in as an array. We can now create a features
directory in app/views/pages
and place our new feature pages in there. Here’s an example of features/one
:
Learn more Ruby on Rails methods: How to Use the “each” Method in Ruby on Rails | Treehouse Quick Tip
One more neat thing is that we have access to Ruby’s methods in these pages. We don’t want to go crazy, but a few little calls here and there aren’t too much of an issue.
For example, if we wanted to add the current year to our home page, we could place the following in the page:
<p>© <%= Date.today.year %> My App.</p>
Which would render the following:
Related Reading: Using Select2 with Ruby on Rails
Learn More About Rails Static Pages With Treehouse
This is just one quick way to implement your own static pages in Rails. If you want to learn more about Ruby on Rails, Treehouse offers a wide range of courses that are chock full of tips and tricks. Sign-up for a free trial of Treehouse today!
Great post and I used it. Thanks for sharing it.
Fogive me for the stupid question, but what is an example of dynamic content on a home page?
If static pages are not supposed to make database calls, however isn’t a returning user signing in on the home page with it’s credentials stored in the database breaking this rule?
Feel free to scathingly correct me, I’m waiting…
Handling Not Found Errors.
I handle this in a different way. In the routes file, I have as the last entry
get ‘*path’ => ‘application#routing_error’
In the application controller I have
def routing_error
render template: ‘404’
end
My 404 file just sits in /views
I’d rather use
`template_exists?` (as suggested by Gregor)
AND
`raise ActionController::RoutingError.new(“No route matches [GET] \”pages/#{params[:page]}\””)`
Instead of
`File.exist?`
`render file: “public/404.html”, status: :not_found`
http://stackoverflow.com/questions/2385799/how-to-redirect-to-a-404-in-rails
How can i set up a link in app/views/pages/home.html.erb which would go to app/views/pages/about.html.erb
Hi!
I’m using minitest to test rather than rspec and I’m new!
My controller test still works to test bringing back the home page if I do :home, how do I write a test for :about? Essentially this test:
test “should get about” do
get :about
assert_response :success
end
Gets this error: ActionController::UrlGenerationError: ActionController::UrlGenerationError: No route matches {:action=>”about”, :controller=>”welcome”}
Hi there! As this is an older post, I’d recommend hopping over to the Treehouse Community to ask your question. Treehouse students and teachers are always happy to help.
@neymarsabin, you can just write smth like this:
How do i define the link_to pattern in other pages as we dont know what are the routes??
you can just write smth like this: link_to “End page”, “/pages/end_page”
When I tried this – it only worked as long as I wasn’t already viewing another /pages/page
So for example, I would start from home and goto a link for /pages/terms (for terms of service) which is in my footer. I also have a pages/privacy page for privacy policy.
The problem is if I click on /pages/terms and then click on the privacy link, it turns it into /pages/pages/terms – which isn’t valid – there’s an extra pages in there! 🙂
Jeff:
You may have already figured this out, but for anyone that runs into this issue later on, the reason for this is your link was relative to your current path.
If you are using (note the path does *not* start with a slash), when you come from some url at your site, for example:
mywebsite.com/pages/my_other_page
then your link_to will replace the current page of the url, but not the current folder, and then it will append pages/my_page to the url you’re currently at and you’ll end run into the problem you mentioned you were having, where you end up at some url like:
mywebsite.com/pages/pages/my_page
On the other hand, if you use (note the path *does* start with a slash), regardless of what page you were coming from, you will be linked to:
mywebsite.com/pages/my_page
Hope that helps!
Apparently it didnt like the example urls I was giving in some spots, Im not sure if it thought I was spamming with links to a website or something, but now the post reads funny and I can’t edit it.
So just to quickly summarize what I was saying:
When using the link_to helper, if your path starts with a slash, it is an absolute path and you will not have the issue. The url will be mywebsite/path, for whatever path you specify.
If you do not use a slash, the link will be relative, and will replace the current page, but not the folder (controller), in the url, so you’ll end up with a duplicate folder in the url, which is the issue you were having. For example, if you were at the url mywebsite/pages/some_page, and the path of your link_to helper was ‘pages/some_other_page’, you would end up at the url mywebsite/pages/pages/some_other_page.
Hope that helps!
Of course this blog isn’t finished, but it is a working Rails application. Some things to consider are giving it some style with CSS, formatting with RedCloth or similar, adding real authentication with Devise, etc.
Thanks for sharing. You could just use template_exists? “pages/#{params[:page]}” instead of File.exist?. Cheers
I think so, that seems like a good idea.
Rails is such an amazing thing for developing applications rapidly.
These are pseudo static pages though. Most of all, they don’t take any advantage of a static page: hit rails router, controller and even ruby inside the page. Either put in there some STRONG caching mechanisms or make something different, because these are just dynamic pages not interacting with the database. A static homepage usually is made to reduce database pressure or rails server pressure, so either cache a static html page every x hours and serve it from apache/nginx or rename the article because I believe it’s deceiving less experienced people
Great article.
The author here is not using the term static in the technical programming taxonomy, rather in the sense timeless vs scheduled or with potential expiration. If one were to pick at the technical nuances of static, even plain html doesn’t present itself, it is dynamically delivered, styled, and displayed. But, of course, no one is thinking about those meanings of static vs dynamic.