The fastest, framework-agnostic conditional CSS class builder for Ruby.
Perfect for ViewComponent, Phlex, Tailwind CSS or just standalone.
Inspired by the JavaScript clsx package. Works with any Ruby codebase.
bundle add clsx-rubyOr add it manually to the Gemfile:
gem 'clsx-ruby', '~> 1.1'Then use it:
require 'clsx'
Clsx['btn', 'btn-primary', active: is_active, disabled: is_disabled]
# => "btn btn-primary active" (when is_active is truthy, is_disabled is falsy)For Rails integration (adds clsx and cn helpers to all views), see clsx-rails.
2-3x faster than Rails class_names across every scenario:
| Scenario | clsx-ruby | Rails class_names |
Speedup |
|---|---|---|---|
| String array | 1.3M i/s | 369K i/s | 3.4x |
| Multiple strings | 1.3M i/s | 408K i/s | 3.3x |
| Single string | 2.4M i/s | 893K i/s | 2.7x |
| Mixed types | 912K i/s | 358K i/s | 2.5x |
| Hash | 1.7M i/s | 672K i/s | 2.5x |
| String + hash | 1.2M i/s | 565K i/s | 2.1x |
Ruby 4.0.1, Apple M1 Pro. Reproduce: bundle exec ruby benchmark/run.rb
| Feature | clsx-ruby | Rails class_names |
|---|---|---|
| Conditional classes | ✅ | ✅ |
| Auto-deduplication | ✅ | ✅ |
| 2–3× faster | ✅ | ❌ |
Returns nil when empty |
✅ | ❌ (returns "") |
| Complex hash keys | ✅ | ❌ |
| Framework-agnostic | ✅ | ❌ |
| Zero dependencies | ✅ | ❌ |
~100 lines of code. Zero runtime dependencies. Ruby 3.2+.
require 'clsx'
Clsx['foo', 'bar']
# => 'foo bar'
Clsx['foo', bar: true, baz: false]
# => 'foo bar'
Clsx['btn', 'btn-primary', active: is_active, disabled: is_disabled]
# => 'btn btn-primary active' (when is_active is truthy, is_disabled is falsy)Cn['foo', bar: true]
# => 'foo bar'Cn is defined only if the constant is not already taken.
include Clsx::Helper
clsx('foo', 'bar')
# => 'foo bar'
cn(hidden: @hidden, 'text-bold': @bold)
# => 'hidden text-bold' (when both are truthy)Clsx.clsx('foo', 'bar')
# => 'foo bar'
Clsx.cn('foo', bar: true)
# => 'foo bar'# Strings (variadic)
Clsx['foo', true && 'bar', 'baz']
# => 'foo bar baz'
# Hashes
Clsx[foo: true, bar: false, baz: a_truthy_method]
# => 'foo baz'
# Hashes (variadic)
Clsx[{ foo: true }, { bar: false }, nil, { '--foobar': 'hello' }]
# => 'foo --foobar'
# Arrays
Clsx[['foo', nil, false, 'bar']]
# => 'foo bar'
# Arrays (variadic)
Clsx[['foo'], ['', nil, false, 'bar'], [['baz', [['hello'], 'there']]]]
# => 'foo bar baz hello there'
# Symbols
Clsx[:foo, :'bar-baz']
# => 'foo bar-baz'
# Numbers
Clsx[1, 2, 3]
# => '1 2 3'
# Kitchen sink (with nesting)
Clsx['foo', ['bar', { baz: false, bat: nil }, ['hello', ['world']]], 'cya']
# => 'foo bar hello world cya'<%= tag.div class: Clsx['foo', 'baz', 'is-active': @active] do %>
Hello, world!
<% end %>erb :"<div class='#{Clsx['nav', active: @active]}'>...</div>"class AlertComponent < ViewComponent::Base
include Clsx::Helper
def initialize(variant: :info, dismissible: false)
@variant = variant
@dismissible = dismissible
end
def classes
clsx("alert", "alert-#{@variant}", dismissible: @dismissible)
end
end<div class="<%= classes %>">...</div>class NavLink < ViewComponent::Base
include Clsx::Helper
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
endclass 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
end-
Returns
nilwhen no classes apply (not an empty string). This prevents rendering emptyclass=""attributes in template engines that skipnil:Clsx[nil, false] # => nil
-
Deduplication — Duplicate classes are automatically removed, even across multi-token strings:
Clsx['foo', 'foo'] # => 'foo' Clsx['foo bar', 'foo'] # => 'foo bar'
-
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
bin/setup # install dependencies
bundle exec rake test # run tests
bundle exec ruby benchmark/run.rb # run benchmarksBug reports and pull requests are welcome on GitHub at https://github.com/svyatov/clsx-ruby.
The gem is available as open source under the terms of the MIT License.