Overview of hypertext
main#![no_std] environments.repository·main·Indexed 42 days ago
https://github.com/vidhanio/hypertextA high-performance, type-checked HTML macro crate for Rust providing compile-time validation for elements and attributes. It features two primary macros, `maud!` for attribute-based syntax and `rsx!` for JSX-like syntax, and supports standard HTML, SVG, MathML, and extensible frameworks like htmx and Alpine.js. Designed for speed with lazy rendering and `#![no_std]` compatibility.
#![no_std] environments.push_str calls.push_str calls into one when no dynamic content exists between them.#![no_std] compatible; allocation is optional via maud::simple! and rsx::simple! if no dynamic content is used.Hypertext categorizes elements into three distinct kinds to enforce correct markup structure:
Normal: Elements that must have a closing tag and are allowed to have children (e.g., <div>).Void: Elements that cannot have children and cannot have a closing tag (e.g., <img>).Xml: Elements used in XML-based languages (like SVG). These can either have a closing tag with children OR be self-closing (e.g., <circle /> or <g>...</g>).Hypertext uses Rust's type system to validate HTML/SVG/MathML elements and attributes at compile time.
Elements are validated by searching for a unit struct with the same name that implements the Element trait. The library searches your current scope first, then falls back to a module named hypertext_elements. This module is typically available via use hypertext::prelude::*;.
Attributes are validated by accessing a constant on the element type that matches the attribute name.
If you want to skip type-checking for an attribute (e.g., if it cannot be parsed or you don't care about validation), you can:
"my-unvalidated-attr"="value").data-* attributes, which are never validated.To define your own validated elements, use the provided define_*_elements! macros within the appropriate module (see below).
The Context trait is a marker trait used to distinguish between different rendering targets in hypertext. This ensures type safety by preventing the accidental use of node-specific logic in attribute contexts, and vice versa.
There are two primary types of contexts:
Node<K>: Represents a complete markup node (like an element) within a specific markup language K. By default, Node uses Html as its kind.AttributeValue: Represents a value intended for an attribute. These values are expected to be eventually wrapped in double quotes.Escaping Requirements:
Node, you must escape & to &, < to <, and > to >.AttributeValue, you must escape & to &, < to <, > to >, and " to ".The Rendered<T, K> type represents a string that has already been processed into HTML. This type is returned by .render() and Raw<T, K>::rendered().
Note: Rendered does not implement Renderable. To avoid the anti-pattern of rendering to a string and then embedding that string into another page, use RenderableExt::memoize or Raw instead.
The NodeKind trait is used to parameterize Node<K> to specify which markup language is being constructed. The following marker types are available:
Html: The default kind for HTML nodes.Svg: Used for SVG nodes.MathMl: Used for MathML nodes.These are used to create specialized type aliases for convenience.
The GlobalAttributes trait provides access to standard HTML attributes like class, id, style, and tabindex. This trait is implemented for all standard HTML elements. To use these attributes in your templates (e.g., with maud!), ensure the trait is in scope, typically by importing hypertext::prelude::*.
use hypertext::prelude::*;
// Standard attributes are available on all elements
// e.g., class, id, style, title, etc.The Buffer<C> type is a wrapper around String used for efficient and safe HTML rendering. It uses a phantom type C (implementing the Context trait) to enforce different escaping rules depending on the intended output context (e.g., standard HTML nodes, attribute values, SVG, or MathML). This prevents accidental XSS vulnerabilities by requiring explicit opt-ins when using raw strings.
use hypertext::Buffer;
use hypertext::context::Node;
let mut buffer = Buffer::<Node>::new();
// ... use buffer to render contentIf a frontend library is not supported by hypertext, you can define your own attribute trait by implementing it for any type that satisfies GlobalAttributes. Note that when using these in macros like maud!, the validator automatically converts hyphens (-) to underscores (_).
use hypertext::{prelude::*, validation::Attribute};
trait MyLibraryAttributes: GlobalAttributes {
const my_attr: Attribute = Attribute;
const my_other_attr: Attribute = Attribute;
}
impl<T: GlobalAttributes> MyLibraryAttributes for T {}
// Usage in template:
// maud! { div my-attr="value" my-other-attr="other value" { "Hello" } }To define custom HTML elements with compile-time validation, use the define_elements! macro.
Important: This macro must be called from within a module named hypertext_elements.
Example:
mod hypertext_elements {
use hypertext::define_elements;
// Re-export all standard HTML elements to ensure they are available
pub use hypertext::validation::hypertext_elements::*;
define_elements! {
/// A custom web component
simple_greeting {
/// The name of the person to greet
name
}
}
}
// Usage:
use hypertext::prelude::*;
// maud! will now recognize `simple-greeting` and validate the `name` attributemod hypertext_elements {
use hypertext::define_elements;
// Re-export all standard HTML elements
pub use hypertext::validation::hypertext_elements::*;
define_elements! {
/// A custom web component that greets the user.
simple_greeting {
/// The name of the person to greet.
name
}
/// An element representing a coordinate.
coordinate {
/// The x coordinate.
x
/// The y coordinate.
y
}
}
}
// Now, you can use the custom elements like this:
use hypertext::prelude::*;
assert_eq!(
maud! {
simple-greeting name="Alice" {
coordinate x=1 y=2 {}
}
}
.render()
.as_inner(),
r#"<simple-greeting name="Alice"><coordinate x="1" y="2"></coordinate></simple-greeting>"#,
)To define custom MathML elements, use the define_mathml_elements! macro.
Important: This macro must be called from within a module named hypertext_mathml_elements.
Example:
mod hypertext_mathml_elements {
use hypertext::define_mathml_elements;
pub use hypertext::validation::hypertext_mathml_elements::*;
define_mathml_elements! {
custom_op {
my_attr
}
}
}mod hypertext_mathml_elements {
use hypertext::define_mathml_elements;
// Re-export all standard MathML elements
pub use hypertext::validation::hypertext_mathml_elements::*;
define_mathml_elements! {
/// A custom MathML element.
custom_op {
/// A custom attribute.
my_attr
}
}
}
// Now, you can use the custom elements like this:
use hypertext::prelude::*;
assert_eq!(
mathml::maud! {
custom-op my-attr="value";
}
.render()
.as_inner(),
r#"<custom-op my-attr="value"/>"#,
)