Open-source project
countries/country_select avatar
countries/country_select

country_select turns ISO 3166-1 codes into a Rails country dropdown

Gemification of rails's country_select

729 stars197 forksRubyMIT

At a glance

What is it?
The country_select gem gives a Rails form a country dropdown whose option values are ISO 3166-1 alpha-2 codes, with priority ordering, filtering, custom formatters and locale aware names. It is a form helper with a small surface, and the README is candid about the naming choices it makes on your behalf.
Who is it for?
Adopt country_select when your Rails form needs a country dropdown whose stored value is a stable alpha-2 code and whose displayed name follows the user's locale. Do not adopt it when you need attributes the ISO list cannot give you, such as currency or calling codes, or when your user base reads country names the ISO list renders in a way you cannot defend, since the README raises exactly that point.
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 63 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 October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Option values are codes, display strings are names

The gem provides a helper that renders an HTML select list of countries following the ISO 3166-1 standard. The repository description calls it the gemification of Rails' own country_select, and the README gives the rule that shapes every use: the option tags use ISO 3166-1 alpha-2 codes as values and the country names as display strings, so the United States appears as `<option value="US">United States of America</option>`.

That single design decision is what the README recommends for storage as well. It says this is the only way to use country_select as of version 2.0, and that it is the recommended way to store your country data since it will be resistant to country names changing. The point is not cosmetic. A name is a string that a standards body, a translation catalogue or a political decision can change, and a code is not.

So the helper is really two things: a dropdown, and a recommendation about your schema. If your model column holds a display name today, adopting the gem means a migration as well as a helper call.

Installing as a gem or pinning it in the Gemfile

Installation is the standard Ruby gem story. Globally:

shell
gem install country_select

Or in a Rails application, as a Gemfile entry:

ruby
gem 'country_select', '~> 11.0'

The version constraint matters because the gem has moved through major versions with behaviour changes, and the README links an UPGRADING.md file with what it calls an important message about upgrading from 1.x. The tag history shows the pace: 11.0.0 published on 2025-05-31, 10.0.1 on 2025-01-04 and 10.0.0 on 2024-09-29. A major bump roughly every year means the constraint `~> 11.0` is a deliberate choice to sit inside one generation of behaviour.

The gem depends on the countries gem for the data and the translations, which the README credits for the automatic localization. The licence is MIT, and the copyright line reads Copyright (c) 2008 Michael Koziarski, released under the MIT license, which dates the original helper well before the gem was extracted.

priority_countries, only, except and the sort_provided switch

The helper's options are where most of the real decisions live, and they come in pairs. The simplest call supplies a model and an attribute:

ruby
country_select("user", "country")

Inside a `form_for` block it behaves like any other form element:

ruby
<%= form_for User.new, url: root_url do |f| %>
  <%= f.country_select :country_code %>
<% end %>

To float a few countries to the top you pass `priority_countries`, and to control the ordering inside the list you have a second switch:

ruby
country_select("user", "country", priority_countries: ["GB", "FR", "DE"]) # Countries will be sorted by name according to the current locale
# or
country_select("user", "country", priority_countries: ["GB", "FR", "DE"], sort_provided: false) # Countries will be displayed is the provided order

The same pairing applies to `only`, which restricts the list to the countries you name, and to `except`, which removes them. With `sort_provided` at its default the remaining countries are sorted by name in the current locale, which is why a list that looks alphabetical in English will not look alphabetical in German. Setting `sort_provided: false` keeps the order you wrote, which is what you want for a curated list of a handful of options but looks arbitrary for a full country list.

Showing a hint without pre-selecting a country

A common requirement is to suggest one country based on context, for instance the country an IP address resolves to, while still letting the user pick something else. The README shows the pattern explicitly, and it is careful about the difference between a hint and a choice:

ruby
country_hint = @country_hint # An optional, validated ISO alpha-2 country code
suggested = country_hint.present? ? [country_hint] : []

country_select(
  "user",
  "country",
  priority_countries: suggested,
  except: suggested,
  include_blank: "Select a country"
)

The same country appears in `priority_countries` and in `except`, so it is offered once at the top of the list rather than twice, and the blank option is labelled `Select a country`. The README explains the result: this displays a usable hint once at the top while leaving the blank option selected until the user chooses. When there is no hint, `suggested` is empty and the ordinary country list is unchanged.

Two pieces of advice in that section matter for correctness. Resolve and validate the hint in the application, and do not pass `selected` unless a value has already been chosen. Pre-selecting a country on the user's behalf records a country the user never picked, which in a checkout or compliance form is a data problem rather than a convenience.

Custom formatters can add attributes to each option

If the plain name is not enough, the gem lets you register a formatter that receives an `ISO3166::Country` object from the countries gem. There are two shapes of return value, and the README is explicit about the difference.

A formatter that returns a string customises the text in the option tag while the value attribute stays unchanged:

ruby
# config/initializers/country_select.rb

# Return a string to customize the text in the <option> tag, `value` attribute will remain unchanged
CountrySelect::FORMATS[:with_alpha2] = lambda do |country|
  "#{country.iso_short_name} (#{country.alpha2})"
end

A formatter that returns an array customises the option text, the value and other HTML attributes:

ruby
# Return an array to customize <option> text, `value` and other HTML attributes
CountrySelect::FORMATS[:with_data_attrs] = lambda do |country|
  [
    country.iso_short_name,
    country.alpha2,
    {
      'data-country-code' => country.country_code,
      'data-alpha3' => country.alpha3
    }
  ]
end

Both are then referenced by symbol:

ruby
country_select("user", "country", format: :with_alpha2)
country_select("user", "country", format: :with_data_attrs)

This is the extension point to reach for when your front end needs the alpha-3 code or a country code in a data attribute, since the standard options carry only the alpha-2 value.

Defaults can be set once in an initializer

For applications that render country selects in many places, the README describes configurable overridable defaults. You can set defaults for `except`, `format`, `locale`, `only` and `priority_countries` in an initializer file:

ruby
# config/initializers/country_select.rb

CountrySelect::DEFAULTS[:except] = [ "ZZ" ]

The README explains the effect of that example: it would exclude "ZZ" from every `country_select` tag where no `except` option is given.

That pattern is the right one for housekeeping codes, an entry that exists in the ISO list for something that is not a country a user should be able to choose. The default is a floor rather than a ceiling, since any call that passes its own `except` overrides it. The same mechanism lets an application fix a locale for all forms, or a default format, without touching the views.

One thing the defaults do not cover is validation. The README is clear that a country code coming from outside, such as a hint resolved from a request, has to be validated in the application, because the helper renders what you give it.

Eight locales, an English fallback, and code based storage

Names are localized automatically from the value of `I18n.locale`, thanks to the countries gem. The README lists the current translations as en, de, es, fr, it, ja and nl, and states that in the event a translation is not available it will revert to the globally assigned locale, by default "en".

A locale can be overridden for a single call, which is what you want when the form's language differs from the request's:

ruby
country_select("user", "country_code", locale: 'es')

Reading a name back out of the stored code is a separate, small piece of work. The README shows a model method that looks the country up and prefers the translated name:

ruby
class User < ActiveRecord::Base

  # Assuming country_select is used with User attribute `country_code`
  # This will attempt to translate the country name and use the default
  # (usually English) name if no translation is available
  def country_name
    country = ISO3166::Country[country_code]
    country.translations[I18n.locale.to_s] || country.common_name || country.iso_short_name
  end

end

Note the fallback chain in that method: the current locale's translation, then the common name, then the ISO short name. The same chain explains why the gem pushes you toward storing codes. A stored name freezes one language's wording into your database, and the helper cannot fix it later.

The repository, its tests and the demo application

The project is a conventional Ruby gem with a gemspec at the root, a `Rakefile`, a `spec/` directory, a `.rspec` file and a `.rubocop.yml` for style rules, plus a `CHANGELOG.md`, an `UPGRADING.md` and an `MIT-LICENSE`. There is a `gemfiles/` directory holding the gemfiles for several Rails versions, and a GitHub Actions workflow for tests along with a CodeQL analysis workflow, both visible as badges in the README header.

Tests run through bundler and rake:

shell
bundle
bundle exec rake

The README explains that the default rake task runs the tests against multiple versions of Rails, which is why the gemfiles need occasional updating, especially when the dependencies in the gemspec change. The maintenance loop for those gemfiles is:

shell
for i in gemfiles/*.gemfile
do
BUNDLE_GEMFILE=$i bundle install --local
done

For evaluating the options in a browser, an example Rails application lives in the countries/country_select_demo repository and is deployed at country-select-demo.onrender.com, with the view files that demonstrate each option under `app/views/demo`.

The honest caveat is the one the README raises first: the ISO 3166 standard is a relatively neutral source of country names, but it may still offend some users, and developers are strongly advised to evaluate the suitability of the list given their user base. That is a product decision this library cannot make for you.

Editorial conclusion

Adopt country_select when your Rails form needs a country dropdown whose stored value is a stable alpha-2 code and whose displayed name follows the user's locale. Do not adopt it when you need attributes the ISO list cannot give you, such as currency or calling codes, or when your user base reads country names the ISO list renders in a way you cannot defend, since the README raises exactly that point. Verify first that the locale you need is among the seven the gem translates, `en`, `de`, `es`, `fr`, `it`, `ja` and `nl`, that your model column stores the code and not the name, and that the version you pin matches the 11.0.0 tag from 2025-05-31, given that the last push was on 2026-08-21.

Frequently asked questions

How do I install the country_select gem in Rails?

The README gives two options, `gem install country_select` for a global install, or a Gemfile entry `gem 'country_select', '~> 11.0'` for an application. The gem's latest listed release is 11.0.0 from 2025-05-31.

What value does country_select store for each option?

The option tags use ISO 3166-1 alpha-2 codes as values, with country names as display strings, so the United States renders as `<option value="US">United States of America</option>`. The README says storing codes is the recommended approach since it is resistant to country names changing.

How do I limit the countries in a country_select?

Use `only` to supply just the countries you want, `except` to discard specific ones, and `priority_countries` to float some to the top of the list. By default the countries are sorted by name in the current locale, while `sort_provided: false` keeps the order you wrote.

Which languages does country_select translate country names into?

The README lists en, de, es, fr, it, ja and nl as the current translations, applied from the value of `I18n.locale` through the countries gem. If a translation is not available it falls back to the globally assigned locale, which is English by default, and a single call can override the locale.

Official sources

  1. countries/country_select on GitHub
  2. License: MIT
  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/countries-country-select.svg)](https://hysenlabs.com/projects/countries-country-select)

Was this useful?