# How to Use Alpine AJAX Integration for Partial HTML Updates in Topcoat

> Learn how to use Alpine AJAX integration for partial HTML updates in Topcoat. Enable the alpine-ajax feature and efficiently update your web page fragments.

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

---

**Enable the `alpine-ajax` feature in your [`Cargo.toml`](https://github.com/tokio-rs/topcoat/blob/main/Cargo.toml), load the Alpine AJAX and Alpine.js scripts with `defer` attributes, then use `ajax_request(cx)` to detect AJAX requests and return only the fragments requested via `ajax_target(cx, "id")` or `ajax_targets(cx)`.**

The tokio-rs/topcoat framework provides first-class support for Alpine AJAX integration, allowing you to build server-rendered Rust applications with partial HTML updates without writing custom JavaScript. By leveraging the `topcoat-alpine-ajax` module, you can inspect request headers to return precisely the fragments the client expects, yielding snappy UI updates while maintaining progressive enhancement.

## Enabling the Alpine AJAX Feature

To access the integration helpers, enable the feature flag in your [`Cargo.toml`](https://github.com/tokio-rs/topcoat/blob/main/Cargo.toml). This exposes the `topcoat::alpine_ajax` module behind the `alpine-ajax` feature gate.

```toml
[dependencies]
topcoat = { version = "0.5.0", features = ["alpine-ajax"] }

```

You must also load the client-side scripts in your layout. Alpine AJAX must load **before** Alpine.js, and both require the `defer` attribute to ensure they run after the document parses.

```rust
view! {
    <head>
        <script defer src="https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.12.4/dist/cdn.min.js"></script>
        <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/dist/cdn.min.js"></script>
    </head>
}

```

## Detecting Alpine AJAX Requests

When Alpine AJAX initiates a request, it sends the `X-Alpine-Request: true` header. In [`crates/topcoat-alpine-ajax/src/request.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-alpine-ajax/src/request.rs), the `ajax_request` function checks for this header.

Use this in your layout function to distinguish between full-page renders and partial updates:

```rust
use topcoat::alpine_ajax::ajax_request;
use topcoat::context::Cx;
use topcoat::router::layout;
use topcoat::view::view;

#[layout]
async fn root(cx: &Cx, slot: Result<impl IntoResponse, Response>) -> Result<Response, Response> {
    if ajax_request(cx) {
        // Return only the page content for AJAX responses.
        return slot;
    }

    // Full-page render for standard navigation.
    view! {
        <html>
            <head>
                <script defer src="https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.12.4/dist/cdn.min.js"></script>
                <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/dist/cdn.min.js"></script>
            </head>
            <body>
                <nav>"Navigation"</nav>
                <main>(slot?)</main>
            </body>
        </html>
    }
}

```

## Targeting Specific Fragments

Alpine AJAX sends the `X-Alpine-Target` header containing space-separated element IDs that need refreshing. Topcoat provides two helpers in [`request.rs`](https://github.com/tokio-rs/topcoat/blob/main/request.rs) to inspect these targets:

- **`ajax_targets(cx)`** – Returns an iterator over all requested IDs.
- **`ajax_target(cx, id)`** – Returns `true` if a specific ID is requested.

Use these to conditionally render fragments:

```rust
use topcoat::alpine_ajax::{ajax_targets, ajax_target};
use topcoat::context::Cx;

// Check if a specific fragment is requested.
if ajax_target(cx, "comments") {
    // Render only the comments list...
}

// Or iterate over all targets.
for target in ajax_targets(cx) {
    println!("Client requested refresh of: {}", target);
}

```

## Accessing Raw Header Constants

If you need manual header access, [`crates/topcoat-alpine-ajax/src/header.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-alpine-ajax/src/header.rs) exposes the header names as `HeaderName` constants:

```rust
use topcoat::alpine_ajax::header::{X_ALPINE_REQUEST, X_ALPINE_TARGET};

// Manual header inspection.
if let Some(targets) = cx.request().headers().get(X_ALPINE_TARGET) {
    // Parse targets manually...
}

```

## Complete Partial Update Handler

Combine these helpers to return only the fragments requested. This example from [`examples/alpine-ajax/src/main.rs`](https://github.com/tokio-rs/topcoat/blob/main/examples/alpine-ajax/src/main.rs) demonstrates returning a form fragment when specifically targeted, while always including a global alert that uses Alpine AJAX's `x-sync` attribute:

```rust
use topcoat::{
    Result,
    context::Cx,
    router::{StatusCode, response::{IntoResponse, Response}, route},
    view::view,
    alpine_ajax::ajax_target,
};

#[route(POST "/comments")]
async fn create_comment(cx: &Cx) -> Result<Response, Response> {
    // Process form validation...

    // Render form fragment only when requested.
    let form_fragment = if ajax_target(cx, "comment_form") {
        view! {
            <form id="comment_form" x-target="comment_form comments" method="post" action="/comments">
                <textarea name="body"></textarea>
                <button type="submit">"Post Comment"</button>
            </form>
        }?
    } else {
        view! {}?
    };

    // Always include alert for x-sync elements.
    let alert = view! {
        <div id="alert" x-sync role="status">
            <p>"Comment posted successfully!"</p>
        </div>
    }?;

    (StatusCode::OK, form_fragment, alert).into_response(cx)
}

```

The `x-sync` attribute ensures elements refresh automatically when their ID appears in the response, regardless of the explicit `x-target` list. This is ideal for flash messages living outside the form's target container.

## Summary

- **Enable the feature** by adding `features = ["alpine-ajax"]` to your `topcoat` dependency in [`Cargo.toml`](https://github.com/tokio-rs/topcoat/blob/main/Cargo.toml).
- **Load scripts** with `defer` in the order: Alpine AJAX first, then Alpine.js.
- **Detect requests** using `ajax_request(cx)` from [`crates/topcoat-alpine-ajax/src/request.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-alpine-ajax/src/request.rs) to skip layout rendering for AJAX calls.
- **Filter fragments** with `ajax_target(cx, "id")` or iterate over `ajax_targets(cx)` to return only requested HTML.
- **Access headers** directly via `X_ALPINE_REQUEST` and `X_ALPINE_TARGET` constants from [`crates/topcoat-alpine-ajax/src/header.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-alpine-ajax/src/header.rs) when needed.
- **Use `x-sync`** for global elements like alerts that should update across all requests.

## Frequently Asked Questions

### What is Alpine AJAX and how does it work with Topcoat?

Alpine AJAX is a plugin for Alpine.js that enables HTML-over-the-wire updates by intercepting link clicks and form submissions. When integrated with Topcoat, the client sends `X-Alpine-Request` and `X-Alpine-Target` headers, allowing your Rust handlers to return partial HTML fragments instead of full pages. Topcoat provides helper functions in `topcoat::alpine_ajax` to detect these requests and extract the target IDs.

### How do I check if the current request is an Alpine AJAX request?

Call `ajax_request(cx)` passing the request context. This function, defined in [`crates/topcoat-alpine-ajax/src/request.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-alpine-ajax/src/request.rs), returns `true` when the `X-Alpine-Request` header is present. Use this in your layout functions to conditionally return only the slot content versus the full HTML document.

### Can I return multiple fragments in a single response?

Yes. Combine multiple fragment strings in your response tuple. Alpine AJAX expects all requested fragments (and any `x-sync` elements) in the response body. The client will automatically distribute the HTML to the corresponding element IDs. Use `ajax_targets(cx)` to iterate over all requested IDs when deciding which fragments to generate.

### What is the difference between `ajax_targets` and `ajax_target`?

`ajax_targets(cx)` returns an iterator over all IDs in the `X-Alpine-Target` header, useful when you need to check multiple possible fragments. `ajax_target(cx, "specific-id")` returns a boolean indicating whether a particular ID was requested, which is more ergonomic when you only care about one specific fragment. Both functions are exported from `topcoat::alpine_ajax` and defined in [`crates/topcoat-alpine-ajax/src/request.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-alpine-ajax/src/request.rs).