SEO and user friendly URLs for Rails with FriendlyID
07 May 2017Rails by default uses a numeric id as a parameter for its URL. Sometimes when building a website such as this blog, instead of emmanuelcorrales.com/blog/4 we want the url to be more SEO and user friendly like emmanuelcorrales.com/blog/seo-and-user-friendly-urls-for-rails-with-friendlyid. We can achieve this by using the FriendlyID gem. In this tutorial I'll show you how to use FriendlyID.
Blog app
I'll demonstrate how to use FriendlyID gem by creating a simple blog app. You can download the source code here.
Create a new rails project by executing the command below.
rails new blogAdd this line to the Gemfile.
# Gemfile
gem 'friendly_id', '~> 5.1.0'Install friendly_id by executing the commands below.
bundle installRun the command below to generate a migration that will create a table for the slugs.
rails generate friendly_idGenerate a scaffold for our post with attributes "title", "description" and "slug".
rails generate scaffold post title:string description:text slug:string:uniq
rake db:migrateModify your Post model to look like the code below.
# app/models/post.rb
class Post < ApplicationRecord
extend FriendlyId
friendly_id :title, use: :slugged
endOn your PostController.rb instead of accessing the post object like the code below.
# app/controllers/post_controller.rb
def set_post
@post = Post.find(params[:id])
endAccess it using the friendly method like the code below.
# app/controllers/post_controller.rb
def set_post
@post = Post.friendly.find(params[:id])
endThe app is finished. Run the app.
rails serveGo to http://localhost:3000/posts You can now set your custom slug whenever you edit your post.
Automatically update the slug based on the title
Personally I want the slug to be automatically updated based on the title so whenever I edit the title, I don't have to manually edit the slug. The app already uses the title to generate the slug when creating a new post but updating it doesn't change the slug.
We need to omit the block of code from the partial form where the slug is edited because its sets the value of the slug. The slug must be nil for it to be regenerated.
<%-# Remove this block of code from app/views/posts/_form.html.erb -%>
<div class="field">
<%= f.label :slug %>
<%= f.text_field :slug %>
</div>The partial form with the omitted code should be similar to the code below.
<%-# app/views/posts/_form.html.erb -%>
<%= form_for(post) do |f| %>
<% if post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %>
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>Set the post's slug as nil before it is updated to generate the new slug on update.
def update
@post.slug = nil
@post.update(post_params)
endNow whenever you edit the title of the post the slug will change.