The fastest conditional CSS class builder for Rails — 2-4x faster drop-in replacement for
class_names. Powered by clsx-ruby.
Adds clsx and cn helpers to all views automatically.
Quick Start
bundle add clsx-railsOr add it manually to the Gemfile:
gem 'clsx-rails', '~> 3.0'That's it — clsx and cn are now available in all your views:
<%= tag.div class: clsx('btn', 'btn-primary', active: @active) do %>
Click me
<% end %>Why clsx-rails over Rails class_names?
Faster
2-4x faster than Rails class_names across every scenario:
| Scenario | clsx | Rails class_names
|
Speedup |
|---|---|---|---|
| String array | 1.2M i/s | 317K i/s | 3.9x |
| Multiple strings | 1.3M i/s | 346K i/s | 3.8x |
| Single string | 2.3M i/s | 812K i/s | 2.9x |
| Mixed types | 901K i/s | 331K i/s | 2.7x |
| Hash | 1.7M i/s | 684K i/s | 2.4x |
| String + hash | 1.2M i/s | 550K i/s | 2.1x |
Ruby 4.0.1, Apple M1 Pro. Reproduce: bundle exec ruby benchmark/run.rb
More features
| Feature | clsx-rails | Rails class_names
|
|---|---|---|
| Conditional classes | yes | yes |
| Auto-deduplication | yes | yes |
| 2-4x faster | yes | no |
Returns nil when empty |
yes | no (returns "") |
| Complex hash keys | yes | no |
Short cn alias |
yes | no |
Usage
# Strings (variadic)
clsx('foo', true && 'bar', 'baz')
# => 'foo bar baz'
# Hashes
cn(foo: true, bar: false, baz: a_method_that_returns_true)
# => 'foo baz'
# Hashes (variadic)
clsx({ foo: true }, { bar: false }, nil, { '--foobar': 'hello' })
# => 'foo --foobar'
# Arrays
cn(['foo', nil, false, 'bar'])
# => 'foo bar'
# Arrays (variadic)
clsx(['foo'], ['', nil, false, 'bar'], [['baz', [['hello'], 'there']]])
# => 'foo bar baz hello there'
# Kitchen sink (with nesting)
cn('foo', ['bar', { baz: false, bat: nil }, ['hello', ['world']]], 'cya')
# => 'foo bar hello world cya'ERB
<%= tag.div class: clsx('foo', 'baz', 'is-active': @active) do %>
Hello, world!
<% end %>
<div class="<%= clsx('foo', 'baz', 'is-active': @active) %>">
Hello, world!
</div>HAML
%div{class: clsx('foo', 'baz', 'is-active': @active)}
Hello, world!Slim
div class=clsx('foo', 'baz', 'is-active': @active)
| Hello, world!Framework Examples
ViewComponent
class AlertComponent < ViewComponent::Base
def initialize(variant: :info, dismissible: false)
@variant = variant
@dismissible = dismissible
end
def classes
clsx("alert", "alert-#{@variant}", dismissible: @dismissible)
end
endPhlex
class Badge < Phlex::HTML
include Clsx::Helper
def initialize(color: :blue, pill: false)
@color = color
@pill = pill
end
def view_template
span(class: clsx("badge", "badge-#{@color}", pill: @pill)) { yield }
end
endTailwind CSS
class NavLink < ViewComponent::Base
def initialize(active: false)
@active = active
end
def classes
clsx(
'px-3 py-2 rounded-md text-sm font-medium transition-colors',
'text-white bg-indigo-600': @active,
'text-gray-300 hover:text-white hover:bg-gray-700': !@active
)
end
endDifferences from JavaScript clsx
-
Returns
nilwhen no classes apply (not an empty string). Rails tag helpers skipnil, preventing emptyclass=""attributes:clsx(nil, false) # => nil
-
Deduplication — duplicate classes are automatically removed:
clsx('foo', 'foo') # => 'foo'
-
Falsy values — in Ruby only
falseandnilare falsy, so0,'',[],{}are all truthy:clsx('foo' => 0, bar: []) # => 'foo bar'
-
Complex hash keys — any valid
clsxinput works as a hash key:clsx([{ foo: true }, 'bar'] => true) # => 'foo bar'
-
Ignored values — boolean
trueandProc/lambda objects are silently ignored:clsx('', proc {}, -> {}, nil, false, true) # => nil
Looking for a framework-agnostic version?
See clsx-ruby — works with Rails, Sinatra, Hanami, or plain Ruby.
Supported Versions
Ruby 3.2+ and Rails 7.2+.
Development
bin/setup # install dependencies
bundle exec rake test # run tests
bundle exec ruby benchmark/run.rb # run benchmarksContributing
Bug reports and pull requests are welcome on GitHub at https://github.com/svyatov/clsx-rails.
License
The gem is available as open source under the terms of the MIT License.