| Crates.io | classnames-rs |
| lib.rs | classnames-rs |
| version | 0.1.0 |
| created_at | 2025-05-30 08:45:43.142467+00 |
| updated_at | 2025-05-30 08:45:43.142467+00 |
| description | A Rust implementation of the classnames utility for conditionally joining class names. |
| homepage | |
| repository | https://github.com/flattoride/classnames-rs/tree/main/crates/classnames-rs |
| max_upload_size | |
| id | 1694822 |
| size | 34,107 |
A Rust macro library for dynamically building CSS class names, inspired by the JavaScript classnames library.
choose!, when!, maybe!Add this to your Cargo.toml:
[dependencies]
classnames-rs = "0.1.0"
use classnames_rs::classnames;
// Basic usage
let classes = classnames!("btn", "btn-primary");
assert_eq!(classes, "btn btn-primary");
// Conditional classes
let is_active = true;
let classes = classnames!(
"btn",
(is_active, "active")
);
assert_eq!(classes, "btn active");
All examples below are tested and verified. You can run cargo test to verify them yourself.
use classnames_rs::classnames;
let result = classnames!("btn", "btn-primary");
assert_eq!(result, "btn btn-primary");
use classnames_rs::classnames;
let is_active = true;
let is_small = false;
let result = classnames!(
"btn",
(is_active, "active"),
(is_small, "btn-sm")
);
assert_eq!(result, "btn active");
use classnames_rs::classnames;
let optional_class: Option<&str> = Some("highlight");
let none_class: Option<&str> = None;
let result = classnames!(
"base",
optional_class,
none_class
);
assert_eq!(result, "base highlight");
use classnames_rs::classnames;
let count = 5;
let result = classnames!(
"counter",
if count > 3 { "high" } else { "low" }
);
assert_eq!(result, "counter high");
choose! Macrouse classnames_rs::{classnames, choose};
let is_dark = true;
let result = classnames!(
"theme",
choose!(is_dark, "dark", "light")
);
assert_eq!(result, "theme dark");
when! Macrouse classnames_rs::{classnames, when};
let is_loading = true;
let result = classnames!(
"btn",
when!(is_loading, "loading")
);
assert_eq!(result, "btn loading");
maybe! Macrouse classnames_rs::{classnames, maybe};
let optional: Option<&str> = Some("special");
let result = classnames!(
"base",
maybe!(optional)
);
assert_eq!(result, "base special");
use classnames_rs::{classnames, choose, when};
let is_primary = true;
let size: Option<&str> = Some("large");
let is_disabled = false;
let count = 3;
let result = classnames!(
"btn",
choose!(is_primary, "btn-primary", "btn-secondary"),
size,
when!(is_disabled, "disabled"),
(count > 0, "has-items", "empty")
);
assert_eq!(result, "btn btn-primary large has-items");
| Expression Type | Syntax | Example |
|---|---|---|
| String literals | "class-name" |
"btn" |
| Conditional tuples | (condition, "class") |
(is_active, "active") |
| Ternary tuples | (condition, "true-class", "false-class") |
(is_dark, "dark", "light") |
| Option types | some_option |
Some("highlight") |
| If expressions | if condition { "true" } else { "false" } |
if loading { "spinner" } else { "" } |
| Block expressions | { /* returns Option<T> or &str */ } |
{ get_dynamic_class() } |
classnames!(...) - Main macro for building class nameschoose!(condition, true_val, false_val) - Conditional selectionwhen!(condition, value) - Conditional inclusionmaybe!(option) - Handle Option typespretty_classname!(input) - Normalize whitespaceuse classnames_rs::{classnames, choose, when};
#[derive(Debug)]
struct ButtonProps {
variant: ButtonVariant,
size: Option<ButtonSize>,
disabled: bool,
loading: bool,
}
#[derive(Debug)]
enum ButtonVariant {
Primary,
Secondary,
Danger,
}
#[derive(Debug)]
enum ButtonSize {
Small,
Medium,
Large,
}
impl ButtonProps {
fn class_names(&self) -> String {
let variant_class = match self.variant {
ButtonVariant::Primary => "btn-primary",
ButtonVariant::Secondary => "btn-secondary",
ButtonVariant::Danger => "btn-danger",
};
let size_class = self.size.as_ref().map(|s| match s {
ButtonSize::Small => "btn-sm",
ButtonSize::Medium => "btn-md",
ButtonSize::Large => "btn-lg",
});
classnames!(
"btn",
variant_class,
size_class,
when!(self.disabled, "disabled"),
when!(self.loading, "loading")
)
}
}
// Usage
let button = ButtonProps {
variant: ButtonVariant::Primary,
size: Some(ButtonSize::Large),
disabled: false,
loading: true,
};
assert_eq!(button.class_names(), "btn btn-primary btn-lg loading");
All examples in this README are tested to ensure they work as documented. Run the following to verify:
# Run all tests
cargo test
# Run only documentation tests
cargo test --doc
# Run specific test module
cargo test readme_examples
Important Note: This library has runtime overhead for conditional evaluations. The macro generates code that performs condition checks at runtime.
For zero runtime overhead, consider using classnames-const-rs which provides compile-time class name resolution for static use cases.
Choose the right tool for your use case:
classnames-rs for dynamic, runtime-dependent class namesclassnames-const-rs for static, compile-time class namesContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.