# How to Use the view! Macro for Conditional Attributes and Control Flow in Topcoat

> Master the Topcoat view! macro for conditional attributes and Rust control flow like if, for, and match. Write dynamic HTML templates in Rust efficiently.

- Repository: [Tokio/topcoat](https://github.com/tokio-rs/topcoat)
- Tags: how-to-guide
- Published: 2026-07-31

---

**The `view!` macro in Topcoat lets you write HTML-like templates in Rust that support conditional attributes and full Rust control-flow constructs including `if`, `for`, `match`, and `let` statements.**

The `tokio-rs/topcoat` framework provides a type-safe templating system through the `view!` macro, which transforms HTML-like syntax into Rust code. Unlike traditional template engines that require separate templating languages, Topcoat allows you to leverage native Rust control-flow directly within your markup. This integration enables sophisticated UI patterns with automatic attribute omission for `false` or `None` values.

## Conditional Attributes in view!

Topcoat’s attribute system evaluates expressions to determine whether an attribute should appear in the final output. This behavior is implemented in [`crates/topcoat-view/src/props.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-view/src/props.rs), where the omission logic handles boolean and optional values according to standard HTML semantics.

### Boolean and Optional Attribute Values

When you supply an expression inside parentheses for an attribute value, the macro evaluates it at runtime. If the expression returns `false` or `None`, the attribute is omitted entirely. If it returns `true`, the attribute appears with an empty value. If it returns `Some(value)`, the attribute renders with the supplied value.

```rust
view! {
    <button
        disabled=(is_disabled)
        aria-current=(is_current.then_some("page"))
        title=(maybe_title)
    >
        "Save"
    </button>
}

```

In this example, `disabled` appears only when `is_disabled` is `true`, `aria-current` emits only when `is_current` is true, and `title` renders only when `maybe_title` contains `Some` value. Literal attributes like `disabled=""` are always emitted regardless of value.

### Dynamic Attribute Generation with Control Flow

The `view!` macro supports control-flow constructs directly inside attribute lists. You can use `if`, `for`, and `match` statements to emit zero or many attributes based on runtime conditions.

```rust
view! {
    <a href="/posts"
        if current {
            aria-current="page"
            class="active"
        }
    >"Posts"</a>
}

```

## Control Flow Inside view! Blocks

The macro expansion in [`crates/topcoat-view/src/view.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-view/src/view.rs) supports full Rust control-flow statements within the markup body. Each branch can contain complete markup trees, and the macro generates code that appends nodes to the view builder appropriately. Dynamic text content like `(post.title)` uses the unescaped text handling implemented in [`crates/topcoat-view/src/unescaped.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-view/src/unescaped.rs).

### Conditional Rendering with if/else

Use standard Rust `if` and `else` blocks to toggle between different markup structures.

```rust
view! {
    if user.is_some() {
        <a href="/account">"Account"</a>
    } else {
        <a href="/login">"Sign in"</a>
    }
}

```

### Iterating with for Loops

The `for` construct generates child nodes by iterating over collections. The loop variable is available within the markup block.

```rust
view! {
    <ul>
        for post in posts {
            <li><a href=(post.url)>(post.title)</a></li>
        }
    </ul>
}

```

You can also use `for` loops inside attribute lists to generate multiple attributes dynamically from collections.

```rust
view! {
    <div
        for (name, value) in attrs {
            (name)=(value)
        }
    ></div>
}

```

### Pattern Matching with match

The `match` statement works inside both markup bodies and attribute lists, allowing you to select attributes or content based on enum variants.

```rust
view! {
    <article
        match state {
            State::Open => class="open",
            State::Closed => aria-disabled="true",
        }
    ></article>
}

```

### Variable Bindings with let

Use `let` bindings within attribute lists to compute values once and reference them in multiple places.

```rust
view! {
    <a
        let href = post.url()
        href=(href)
        data-slug=(post.slug)
    > (post.title) </a>
}

```

## Summary

- **Conditional attributes** automatically omit when expressions evaluate to `false` or `None`, and appear when `true` or `Some(value)`.
- **Control-flow constructs** including `if/else`, `for`, `match`, and `let` work inside both markup bodies and attribute lists.
- The macro expansion generates efficient Rust code that calls the `View` builder API in [`crates/topcoat-view/src/view.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-view/src/view.rs).
- **Dynamic attribute generation** allows emitting multiple attributes from collections using `for` loops inside attribute lists.

## Frequently Asked Questions

### Can I use arbitrary Rust expressions inside the view! macro?

Yes, the `view!` macro accepts any valid Rust expression inside parentheses for attribute values or within control-flow blocks. The macro transpiles your markup into standard Rust code, so you can call methods, use match expressions, and access variables from the surrounding scope.

### How does Topcoat handle boolean attributes like disabled or checked?

Topcoat follows standard HTML boolean attribute semantics. When a boolean expression evaluates to `true`, the attribute is rendered with an empty value. When `false`, the attribute is omitted entirely. This logic is implemented in [`crates/topcoat-view/src/props.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-view/src/props.rs).

### Can I nest control-flow statements inside each other?

Yes, you can nest `if`, `for`, and `match` statements arbitrarily within a `view!` block. Each nested construct generates the appropriate Rust control-flow code that appends to the view builder, maintaining full type safety throughout the template hierarchy.

### Where can I find the complete syntax documentation for the view! macro?

The official syntax documentation and examples are located in [`crates/topcoat-view/macro/docs/view.md`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-view/macro/docs/view.md) within the `tokio-rs/topcoat` repository. This file covers all supported constructs including conditional attributes, control-flow integration, and literal value handling.