Using htmx’s hx‑boost for AJAX navigation with graceful fallback
Learn how hx‑boost turns ordinary links and forms into AJAX requests while preserving normal navigation for users without JavaScript, plus a worked example and key trade‑offs.
10 Dec 2025, 03:14 UTC

Problem: Full‑page reloads hurt perceived performance
When a user clicks a navigation link or submits a simple form, the browser discards the current page and loads a new HTML document. Even if the server returns mostly the same layout, the browser must repaint the whole view, which feels slower than swapping only the changed part.
Thesis: hx‑boost gives you AJAX upgrades with a built‑in fallback
The hx-boost attribute tells htmx to intercept clicks on <a> elements and submissions of <form> elements, issue an XMLHttpRequest/fetch, and swap the response into the DOM using htmx’s swap strategy. If htmx fails to load or JavaScript is disabled, the elements fall back to regular HTTP requests, preserving normal navigation.
Enabling and fine‑tuning hx‑boost
Place hx-boost on any element; the most common spot is <body> to boost all links and forms underneath it.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Boosted navigation demo</title>
<script src="https://unpkg.com/htmx.org@1.9.12"></script>
</head>
<body hx-boost>
<nav>
<ul>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
<nav>
<main id="content"></main>
</body>
</html>
To disable boosting for a specific link or form, add hx-boost="false":
<a href="/download" hx-boost="false">Download PDF</a>
Conversely, you can keep boosting off globally and enable it selectively with hx-boost="true" on individual elements.
Server‑side considerations: return only the fragment
When htmx makes an AJAX request it includes the header HX-Request: true. Your endpoint should inspect this header and return only the HTML fragment that needs to be swapped, not a full <html> document. Returning a full page causes the fragment‑swap algorithm to insert an entire document inside the target, duplicating <head>, <body>, and breaking layout.
Example using Node/Express (pseudo‑code, not tested):
app.get('/about', (req, res) => {
const isHtmx = req.get('HX-Request') === 'true';
if (isHtmx) {
// Return only the part that goes inside #content
res.send(`<section><h1>About Us</h1><p>Our story...</p></section>`);
} else {
// Full page for non‑htmx clients
res.render('about', { layout: 'main' });
}
});
If you cannot change the server, you can use htmx’s hx-select to extract a fragment from a full page, but this adds extra client‑side work and still sends unnecessary bytes.
Worked example: boosting a navigation link
- Add the htmx script to
<head>(see the snippet above). - Mark
<body>withhx-boost. - Create a server endpoint that returns a fragment when
HX-Requestis present. - Click the “About” link.
- Open DevTools → Network tab. You should see a request with:
- Request Method: GET
- Request Header:
HX-Request: true - Response: HTML fragment (no
<html>tag) - In the Elements tab, verify that the fragment appears inside
<main id="content">and the URL updates via the browser’s History API (htmx pushes state by default). - Disable JavaScript or block the htmx script and click the link again; the browser should perform a normal navigation to
/aboutand reload the full page.
Where to run: Edit your HTML templates and server route files. No special permissions are required beyond normal file‑system access for your project.
Risks: If the endpoint ignores HX-Request and returns a full document, the swapped content will duplicate page structure, potentially breaking CSS and JavaScript. Test by temporarily removing the header check and observing the DOM.
Trade‑offs and limitations
- Fragment‑only responses: You must adjust server logic to conditionally render partials. This adds a small amount of complexity compared to serving full pages.
- Forms with file uploads: Boosted forms use XHR/fetch, which can handle
multipart/form-data, but you must ensure your server parses the body correctly. Some older frameworks may need extra middleware. - Browser history: htmx pushes state for boosted navigation by default; if you disable this (
hx-push-url="false") you lose the ability to use the back button to restore AJAX‑loaded content. - Graceful degradation: The fallback works only if the htmx script fails to load or is disabled. If JavaScript runs but htmx throws an error (e.g., due to a mismatched swap strategy), the link will not fall back automatically.
Actionable closing
Use hx-boost when you want most navigation to feel instantaneous without writing custom AJAX code, and you can adjust your server to return fragments for AJAX requests. Follow this checklist before deploying:
- Add htmx via CDN or bundle.
- Apply
hx-boost="true"(or on<body>) to the scope you want. - Mark any links/forms that must stay as full‑page navigations with
hx-boost="false". - Update server endpoints to check
HX-Requestand return only the needed HTML fragment. - Verify in DevTools that boosted requests carry the header and swap only the fragment.
- Test the fallback by disabling JavaScript or blocking the htmx script.
- Monitor for duplicate layout issues; if they appear, enforce fragment responses or use
hx-selectas a temporary mitigation.
When these conditions are met, you get faster perceived navigation, progressive enhancement, and minimal custom JavaScript—all with a single attribute.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.