Attributes
While we could build a UI by assembling unstyled elements and text, we typically want to customize their appearance and behavior. This is where we use element attributes. Attributes provide extra information to the renderer on how to display the UI.
You can specify attributes by adding the name of the attribute, a colon, and then the value to the body of an element. For example, we might want to style a div with a particular class and ID, which we add as attributes:
rsx! { div { class: "container", id: "root-container" } }
We can use an attribute to set the type
of an input element. The default type is text
which shows a text input box, but we can set it to number
to only accept numbers:
rsx! { input { type: "number" } }
Just like text nodes, attributes can include formatted segments. We can set the value of the input element to a signal to control it:
let mut value = use_signal(|| "Hello world".to_string()); rsx! { input { value: "{value}" } }
Attribute Scope
Every element has two sets of attributes:
- Global Attributes: attributes which can be applied to every element
- Specific Attributes: attributes that only apply to one specific element
For example, all elements support the id
and class
attributes, but only video
elements support the autoplay
attribute. For a full list of attributes, visit the relevant links above.
IDE Support
RSX provides autocomplete and inline docs support for elements and their attributes. To get autocomplete suggestions, simply start typing in your editor:
We've documented every element with documentation pulled from the Mozilla Developer Docs. Simply hover over the element with your cursor for more information:
The same docs apply for attributes as well:
Non-Text Attributes
Typically, you'll add attributes to your elements using formatted text. However, attributes can accept a wide range of value types, including:
Text
: Formatted text, theString
type, or anything that implementsDisplay
.Float
: Floating point numbers, typically on sliders and inputs.Int
: Integer numbers for discrete gradations.Bool
: A boolean value indicating true/false.Listener
: A Rust callback that will be executed when the attribute is triggered.Any
: A type-erasedRc<dyn Any>
, typically used by 3rd-party renderers.None
: The attribute will be removed from the element entirely.
Most commonly, you might use the bool
attribute to set a boolean state:
rsx! { input { type: "checkbox", checked: true } }
Or the Some
/ None
variant for setting an HTML Boolean Attribute:
rsx! { div { itemscope: Some("scope") } }
Note that Dioxus automatically converts false
for some attributes to None
in order to match the behavior of the HTML Boolean Attribute.
Event Listeners
While some attributes influence how an element is rendered, other attributes influence its interactive behavior. These attributes, called Event Listeners, allow you to respond to user input.
In RSX, event handlers always start with on
. The syntax is the same as normal attributes, but event handlers only accept a closure that responds to the event. We can attach an event listener to the oninput
event of the input element to listen for changes to the input:
rsx! { input { oninput: move |event| { println!("Input changed to: {}", event.value()); }, } }
More information about event listeners including how events bubble and how to prevent default behavior can be found later in the Event Handlers of the Reactivity section.
There are a wide range of event listeners available - see the full HTML list for more details.
Spreading Attributes
Occasionally, the set of attributes you want to pass to an element might either be dynamic or defined elsewhere in your application. In these cases, you can spread attribute lists into elements with the ..
syntax. Typically, lists of attributes will be passed into a component via its Properties, which we cover in a later chapter.
let attributes = vec![ Attribute { name: "id", namespace: None, volatile: false, value: "cool-button".into_value(), } ]; rsx! { button { ..attributes, "button" } }
Attributes lists will be merged in the order they appear, so later attributes in the list take precedence over earlier attributes. Attribute spreading becomes very useful when refactoring your UI into a reusable component libraries.
Special Attributes
Most attributes in RSX are rendered verbatim, but there are a few exceptions. In some cases, RSX deviates from traditional HTML to simplify development or work better with the ecosystem tools.
Conditional Attributes
You can conditionally set an attribute by setting the attribute value to an unterminated if statement. If the if statement evaluates to true, the attribute will be set:
let number_type = use_signal(|| false); rsx! { input { type: if number_type() { "number" } } }
Style attributes
In addition to the standard style
attribute, each style can also be passed as a separate attribute. For example, we can set the color
and font-size
of an element using the color
and font_size
attributes:
rsx! { div { style: "width: 20px; height: 20px; background-color: red; margin: 10px;" } div { width: "20px", height: "20px", background_color: "red", margin: "10px", } }
Class attribute
Most attributes can only be defined once per element, but the class
attribute can be defined multiple times. Each class will be added to the element's class list. This can be convenient when adding many optional classes to an element in a styling system like TailwindCSS:
rsx! { span { class: if red { "bg-red-500" }, class: if blue_border { "border border-blue-500" }, class: "w-4 h-4 block", } }
This feature is especially important when using TailwindCSS since the class compiler does not understand formatted Rust strings when collecting classes. By placing the dynamic class in a sibling attribute, the Tailwind compiler sees both class lists at compile time.
onresize and onvisible
Dioxus provides two custom HTML attributes not found in the HTML specification.
- onresize
- onvisible
Dioxus automatically constructs a cross-platform IntersectionObserver
that emits the respective events for you.
With onresize, you can watch for changes to an element's size and position:
fn app() -> Element { let mut items = use_signal(|| 100); rsx! { // Adding a value will cause the `div` to be re-rendered with an extra div button { onclick: move |_| items += 1, "Add one" } div { // This will be called when the `div` is resized onresize: move |data| { info!("resized to {:#?}", data.get_border_box_size().unwrap()); }, for x in 0..items() { div { "{x}" } } } } }
With onvisible
, you can handle the cases when an element enters and exits the viewport. This is useful for implementing things like lazy loading, infinite scrolls, and virtual lists:
fn app() -> Element { rsx! { div { onvisible: move |data| info!("visibility changed"), "Hello world!" } } }
You can add rich scroll-aware animations to your app without needing to write custom JavaScript.
Custom Attributes
Dioxus has a pre-configured set of attributes that are validated at compile time. If you surround the attribute name in quotes, you can use a custom attribute outside of the pre-defined set:
rsx! { div { "style": "width: 20px; height: 20px; background-color: red;" } }
Note that this works even with event listeners. We occasionally use this to insert small snippets of JavaScript into our apps when writing the corresponding web-sys code might be verbose:
rsx! { button { "onclick": "navigator.clipboard.writeText(window.document.title);", "Copy to clipboard" } }
The HTML Escape Hatch
If you're working with pre-rendered assets, output from templates, or output from a JS library, then you might want to pass HTML directly instead of going through Dioxus. In these instances, reach for dangerous_inner_html
.
dangerous_inner_html
sets the text content of the element to the provided value. This will overwrite any other attributes or children of the element.
For example, shipping a markdown-to-Dioxus converter might significantly bloat your final application size. Instead, you'll want to pre-render your markdown to HTML and then include the HTML directly in your output. We use this approach for the Dioxus homepage:
// this should come from a trusted source let contents = "live <b>dangerously</b>"; rsx! { div { dangerous_inner_html: "{contents}" } }
Note! This attribute is called "dangerous_inner_html" because it is dangerous to pass it data you don't trust. If you're not careful, you can easily expose cross-site scripting (XSS) attacks to your users.
If you're handling untrusted input, make sure to sanitize your HTML before passing it into
dangerous_inner_html
– or just pass it to a Text Element to escape any HTML tags.