Std-compatible HTML formatting macros
fhtml
Provides formatting macros for writing HTML without the annoyance of dealing with HTML inside string literals. A few highlights:
- simplicity: no complex templating syntax, just plain HTML with embedded expressions and format specifiers
- zero extra allocations:
fhtml
macros expand to theirstd
counterpart with no indirections or added allocations - compatibility: since
fhtml
is simply a wrapper overstd
macros, meaning that you can easily use idiomatic Rust, such as implementingfmt::Display
orfmt::Write
for creating components, or integrate with existing libraries and tools - safety:
fhtml
provides an easy way to escape values (escaping is NOT done implicitly)
- HTML is typed as-is, unquoted:
fhtml::format!(<input />);
- Expressions are passed in using braces:
fhtml::format!(<div>{1 + 1}</div>);
- Text nodes are quoted:
fhtml::format!(<p>"Some text"</p>);
- Format specifiers are written after expressions:
fhtml::format!(<code>{vec![1, 2, 3]:?}</code>);
- Escaping is done by using an exclamation mark
!
as a format specifier:
fhtml::format!(<div>{"<b>Dangerous input</b>":!}</div>);
this being the only format specifier deviating from the std::fmt syntax
let mut buffer = String::new();
fhtml::write!(buffer, <div>"Hello, World!"</div>);
let user_input = "<b>Dangerous input</b>";
fhtml::format!(<div>{user_input:!}</div>); // "<div><b>Dangerous input</b></div>"
Since fhtml
macros expands to their std
counterpart, you are free to create components however you prefer
fn heading(label: &'static str) -> String {
fhtml::format! {
<h1>{label}</h1>
}
}
let page = fhtml::format! {
<main>
{heading("My Heading")}
<div>"My Content"</div>
</main>
};
use std::fmt;
struct Product {
name: &'static str,
price: f32,
}
impl fmt::Display for Product {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fhtml::write! { f,
<article>
<h2>{self.name}</h2>
<h3>"$" {self.price}</h3>
</article>
}
}
}
let products = fhtml::format! {
<h1>"Our products"</h1>
{Product {
name: "Arabica Coffee Beans",
price: 3.99
}}
{Product {
name: "Sourdough Bread",
price: 2.49
}}
};
fhtml::format!(<code>{vec![1, 2, 3]:?}</code>); // "<code>[1, 2, 3]</code>"
fhtml::format!(<span>{10:#b}</span>); // "<span>0b1010</span>"
fhtml::format! {
<ul>
{
(0..10).fold(String::new(), |mut f, i| {
let _ = fhtml::write! { f,
<li>{i}</li>
};
f
})
}
</ul>
}