Pretty URLS - How to add slugs to a model using friendly_id

Replacing record IDs in your URLs with the name of your record is incredibly useful for SEO purposes as well as making the links people share more descriptive and self-explanatory.

It’s also very easy using the friendly_id gem.

What is a slug?

A slug is a unique user friendly record identifier. For example in the URL https://www.example.com/users/john-smith, john-smith is the slug generated from the user record’s name.

Installation

First off we’ll want to install friendly_id. We can do that by running gem install friendly_id or adding gem 'friendly_id' to your Gemfile and running bundle install.

After that we’ll want to run rails generate friendly_id to generate the friendly_id config file and generate the migration for setting up the table.

Note: As of writing if you’re running Rails 5.1+ you’ll have to go into the generated migration and add your rails version to the migration so it looks like this:
~~ruby
~~class CreateFriendlyIdSlugs < ActiveRecord::Migration[5.1]
~~

Then you’ll want to run rake db:migrate.

Adding Friendly ID to your model

From here you just need to add this to your model:

~~ruby
~~extend FriendlyId
~~friendly_id :name, use: :slugged
~~

This will use the name column of your table to generate a slug for your record automatically when your save it.

Note that this won’t update if you update the name column. We will cover how to make that happen later.

Using slugs in your URLs

There are two sides to adding slugs to your URLs. In your views when generating the URL and in your controllers when looking up records.

Views

This is as simple as just using .slug instead of .id when generating your paths. For example:

~~ruby
~~foo_path(id: foo.slug)
~~

Instead of

~~ruby
~~foo_path(id: foo.id)
~~

Controllers

To look up a record by a slug you’ll want to use Foo.friendly.find(params[:id]) instead of Foo.find(params[:id]. This searches by slug as well as by the id of the record, allowing old URLs to still work.

If you don’t want to use friendly.find and instead want to make find work as if you had you should modify your friendly_id call on your model to look like this:

~~ruby
~~friendly_id :name, use: [:slugged, :finders]
~~

History

Update slugs on column change

What happens if we have a conflict?

Using a function for the slug

Thanks for subscribing!
Like what I'm doing? Subscribe and I'll let you know when I write new stuff.
Want to unsubscribe?
Subscribe