Model or dataset
norman/friendly_id avatar
norman/friendly_id

FriendlyId: slugging for ActiveRecord models

FriendlyId is the “Swiss Army bulldozer” of slugging and permalink plugins for ActiveRecord. It allows you to create pretty URL’s and work with human-friendly strings as if they were numeric ids for ActiveRecord models.

6,224 stars591 forksRubyMIT

At a glance

What is it?
FriendlyId generates human-readable slugs for ActiveRecord models and lets User.friendly.find resolve them, with optional slug history. It is a Rails gem, MIT licensed, and the README documents the install path and the main options.
Who is it for?
Adopt FriendlyId when you have an ActiveRecord model with a stable name column and you want /users/joe-schmoe instead of /users/4323454, and you are willing to add a slug column plus a migration. Skip it when your routes are not ActiveRecord-backed, when the URL identifier must be opaque, or when you cannot run the data migration that backfills slugs for existing rows.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 43 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem FriendlyId solves in a Rails app

ActiveRecord models are addressed by primary key. A show route for a user ends up as /users/4323454, which tells a reader nothing and makes a link hard to retype or share verbally. FriendlyId adds a second, human-readable identifier to the model and teaches the model's finder to accept it. The README frames the goal with a concrete pair of URLs: https://example.com/states/washington instead of https://example.com/states/4323454.

The audience is Rails developers who own both the model and the route. FriendlyId is not a routing layer and does not generate paths for you; it changes what the model accepts as an identifier, and you keep calling your normal route helpers. It also exposes the slug as an ordinary attribute, so views and controllers can read it the way they read any other column. If your application is not ActiveRecord-based, the gem's central mechanism (a model-level finder override plus a slug column) has nothing to attach to.

How the slug column and friendly.find fit together

The mechanism has two halves. On the model, extend FriendlyId and declare the source attribute and the slugging strategy with friendly_id :name, use: :slugged. That gives the record a slug derived from the name attribute and stored in a slug column on the table. On lookup, User.friendly.find(params[:id]) resolves the argument as a slug first. The README shows a controller replacing User.find with User.friendly.find.

The README lists the advanced features the gem offers: slug history and versioning, i18n, scoped slugs, reserved words, and custom slug generators. Slug history is the one with a storage consequence. The generator command creates a CreateFriendlyIdSlugs migration, and the README states plainly that you can delete that migration if you will not use the slug history feature. That is the clearest signal in the README that history is optional and carries its own table.

The finder has a documented escape hatch for the not-found path. Passing allow_nil: true to friendly.find avoids raising ActiveRecord::RecordNotFound and returns nil instead. The README shows the same three arguments in both modes: a bad slug, a numeric value that is not a valid primary key, and nil. Without the option, all three raise; with it, all three return nil. That is a deliberate trade: you lose the exception that would otherwise surface a broken link as a 404 through Rails' default handling, and you take on the responsibility of deciding what nil means in the action.

Installing the friendly_id gem and getting a first friendly URL

The README's install path starts in the Gemfile. The version constraint shown is 5.5.0 or greater within the 5.x line, and the README notes that 5.0.0 or greater is required for Rails 4.0 and up.

ruby
gem 'friendly_id', '~> 5.5.0'

Then install it:

shell
bundle install

Add the slug column to the table you want to address by slug. The README uses Users as the example and generates a migration with a unique slug column:

shell
rails g migration AddSlugToUsers slug:uniq

Generate the FriendlyId configuration file and its migration:

shell
rails generate friendly_id

If slug history is not part of your plan, the README says you can delete the CreateFriendlyIdSlugs migration. Then run the migrations:

shell
rails db:migrate

Wire the model. The README's example extends FriendlyId and slugs on the name attribute:

ruby
class User < ApplicationRecord
  extend FriendlyId
  friendly_id :name, use: :slugged
end

Change the lookup in the controller from User.find to User.friendly.find:

ruby
class UserController < ApplicationController
  def show
    @user = User.friendly.find(params[:id])
  end
end

After that, creating a record with User.create! name: "Joe Schmoe" makes the show page reachable at http://localhost:3000/users/joe-schmoe. The README does not describe a separate verification command; the URL is the check.

If you are adding FriendlyId to an application that already has rows, the README gives the backfill step: run User.find_each(&:save) from the console, a runner, or a Rake task. The README does not document rollback for these migrations, and it does not describe what happens to rows whose source attribute is blank at backfill time.

Where FriendlyId is the wrong tool

The gem assumes the URL identifier maps to an ActiveRecord record. If your public URLs are served from a static export, a non-Rails service, or a routing layer that does not touch the model, adding FriendlyId buys you nothing.

Slugs are also not opaque identifiers. They are derived from a human-readable attribute, which means they leak that attribute into the URL and change when it changes. The README's feature list includes slug history precisely because old URLs stop resolving when a slug changes, and using history means keeping the CreateFriendlyIdSlugs migration and its table. If you delete that migration for simplicity, you are choosing to break old URLs when names change. That is a real decision, not a detail.

There is a second, quieter cost: the backfill. Adding the gem to a live application means running User.find_each(&:save) over existing rows so they acquire slugs. The README presents this as a one-liner and does not discuss batching, uniqueness collisions during the backfill, or what a row with a duplicate name produces. Uniqueness is enforced at the column level in the README's own migration example (slug:uniq), so collisions surface as database errors rather than as a documented resolution path.

Finally, allow_nil: true is easy to reach for and easy to misuse. It converts every miss, including a nil parameter, into nil. If the action does not distinguish nil from a record, a bad link becomes a blank page instead of a 404.

FriendlyId compared with plain ActiveRecord finders

The alternative most teams actually weigh is not another gem. It is doing nothing: keep numeric ids in the URL, or add a slug column by hand and write a small finder that queries it. That approach has no dependency and no generator, and it is honest about its limits. What it does not give you is the rest of the README's feature list: slug history and versioning, i18n, scoped slugs, reserved words, and custom slug generators. Each of those is a behavior you would otherwise implement yourself, and history in particular means a second table plus the logic to look a retired slug up and redirect.

The other comparison is against routes that carry the human-readable part in the path rather than in the model. Those work when the readable segment is not the record's identity and does not need to survive renames. FriendlyId's difference is that the slug is stored state on the row, findable through the model's own finder, and versioned when you opt into history. That is a heavier design, and it is the reason the gem needs a migration and a backfill while a path segment does not.

Maintenance, licence, and what a FriendlyId upgrade costs

The repository is not archived, and the last push recorded is 2026-08-18. The most recent release in the list is v5.7.0 from 2026-05-08. The README's own Gemfile example pins the 5.5.0 line, so a new application following the README exactly will not be on the latest release unless it raises that constraint.

The licence is MIT, and the README carries the full MIT text with a copyright line for 2008-2020 Norman Clarke and contributors. MIT is permissive: it allows use, modification and distribution provided the copyright notice and permission notice are included. That is the extent of what the README states; it is not legal advice, and the notice obligation is the part worth checking against how your application ships the gem.

Upgrade cost is not documented in the README. The repository does contain an UPGRADING.md at the top level, which is where a version-to-version migration would be described, and a Changelog.md alongside it. The README itself does not discuss rollback, deprecation windows, or what changes between major versions. The README also points to the FriendlyId Guide as the complete documentation, so a team evaluating an upgrade should read UPGRADING.md and the guide rather than the README alone. The README's note that 5.0.0 or greater is required for Rails 4.0 and up is the only compatibility statement it makes.

Editorial conclusion

Adopt FriendlyId when you have an ActiveRecord model with a stable name column and you want /users/joe-schmoe instead of /users/4323454, and you are willing to add a slug column plus a migration. Skip it when your routes are not ActiveRecord-backed, when the URL identifier must be opaque, or when you cannot run the data migration that backfills slugs for existing rows. Before you commit, verify three things: whether you need the slug history feature (which decides whether the CreateFriendlyIdSlugs migration stays), what your uniqueness scope is across the models you extend, and whether your existing rows produce non-empty slugs after User.find_each(&:save).

FriendlyId is a small, focused gem rather than a URL framework. The README points at the FriendlyId Guide for the full documentation, and that guide is where the history, scoped-slug and i18n behavior is described.

Frequently asked questions

What is FriendlyId?

FriendlyId is a slugging and permalink plugin for ActiveRecord, described in the README as the Swiss Army bulldozer of that category. It lets a model be addressed by a human-readable string, so a URL reads /states/washington instead of /states/4323454.

Is FriendlyId safe?

The README does not make a security claim. What it documents is that FriendlyId changes how a model is looked up and stores the slug in a column on your table, so the trust boundary is the same as any other ActiveRecord finder you write.

How do I install the friendly_id gem in a Rails app?

Add gem 'friendly_id', '~> 5.5.0' to the Gemfile, run bundle install, add a slug column with a migration such as rails g migration AddSlugToUsers slug:uniq, run rails generate friendly_id, then rails db:migrate. After that, extend FriendlyId in the model with friendly_id :name, use: :slugged and switch the controller to User.friendly.find.

Do I need the CreateFriendlyIdSlugs migration that friendly_id generates?

Only if you use the slug history feature. The README states that you can delete the CreateFriendlyIdSlugs migration if you will not use slug history.

How do I generate slugs for records that already exist?

The README's instruction is to run User.find_each(&:save) from the console, a runner, or a Rake task. The README does not document what happens to rows whose source attribute is blank or duplicated during that pass.

What does allow_nil: true do in friendly.find?

It makes friendly.find return nil instead of raising ActiveRecord::RecordNotFound. The README shows it applied to a bad slug, a numeric value that is not a valid primary key, and nil, all of which raise without the option.

Official sources

  1. License: MIT
  2. norman/friendly_id on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/norman-friendly-id.svg)](https://hysenlabs.com/projects/norman-friendly-id)