component!() { /* proc-macro */ }Expand description
A procedural macro for defining reusable HTML components.
The component! macro creates a struct and implements the Component trait, enabling composable and reusable
HTML templates.
§Basic usage
use plait::{component, html, render};
component! {
pub fn Card {
div(class: "card") {
#children
}
}
}
let html = render(html! {
@Card {
"Card content"
}
});
// Output: <div class="card">Card content</div>§Syntax
component! {
[visibility] fn ComponentName[<generics>]([props]) [where clause] {
// component body (html! syntax)
}
}§Component props
Define typed props in the function signature:
component! {
pub fn Button<'a>(class: &'a str, size: u32) {
button(class: format_args!("btn {} size-{}", class, size)) {
#children
}
}
}
// Usage:
html! {
@Button(class: "primary", size: 2) {
"Click me"
}
}
// Output: <button class="btn primary size-2">Click me</button>§Special placeholders
§#children - Child content
The #children placeholder renders content passed between the component’s opening and closing tags:
component! {
pub fn Wrapper {
div(class: "wrapper") {
#children
}
}
}
html! {
@Wrapper {
span { "Child 1" }
span { "Child 2" }
}
}
// Output: <div class="wrapper"><span>Child 1</span><span>Child 2</span></div>§#attrs - Attribute spreading
The #attrs placeholder spreads HTML attributes passed to the component. This enables attribute forwarding for
flexible component APIs:
component! {
pub fn Button<'a>(class: &'a str) {
button(class: format_args!("btn {}", class), #attrs) {
#children
}
}
}
// Attributes after `;` are spread via #attrs:
html! {
@Button(class: "primary"; id: "submit", disabled?: true) {
"Submit"
}
}
// Output: <button class="btn primary" id="submit" disabled>Submit</button>§Generics and lifetimes
Components support full Rust generics:
component! {
pub fn List<'a, T: std::fmt::Display>(items: &'a [T]) {
ul {
for item in items {
li { (item) }
}
}
}
}§Component composition
Components can be nested and composed:
component! {
pub fn Button<'a>(class: &'a str) {
button(class: format_args!("btn {}", class), #attrs) {
#children
}
}
}
component! {
pub fn Card {
div(class: "card") {
@Button(class: "card-btn"; #attrs) {
#children
}
}
}
}§Calling components
Components are called using @ComponentName syntax within html!:
html! {
@ComponentName(prop1: value1, prop2: value2; attr1: val1, attr2?: optional) {
// children
}
}The syntax is:
@ComponentName- Component invocation prefix(...)- Optional arguments section- Before
;- Component props (matched to component parameters) - After
;- HTML attributes (spread via#attrs)
- Before
{ ... }- Children content (rendered via#children)
If a component has no props, you can omit everything before the semicolon:
html! {
@Card(; class: "highlighted") {
"Content"
}
}§Generated code
The macro generates:
-
A struct with the component’s props as public fields:
ⓘpub struct Button<'a> { pub class: &'a str, } -
An implementation of the
Componenttrait:ⓘimpl<'a> Component for Button<'a> { fn render( self, f: &mut HtmlFormatter<'_>, attrs: impl FnOnce(&mut HtmlFormatter<'_>), children: impl FnOnce(&mut HtmlFormatter<'_>), ) { // component body } }