Turning Full‑Page Loads into Partial Swaps with htmx hx‑boost
Learn how htmx’s hx‑boost attribute can turn a traditional multi‑page app into a fluid, single‑page‑app‑like experience with minimal JavaScript. The post walks through the problem, the solution, a concrete example, trade‑offs, and how to verify the result.
09 Oct 2026, 05:59 UTC

Problem: Heavy Page Reloads in a Server‑Rendered App
Many web teams ship applications that render the whole page on the server and rely on full navigation for every link or form submission. This guarantees deep‑linking, SEO friendliness, and a single source of truth, but it also causes the browser to download and parse the entire HTML document on each request. Users feel lag, the back button behaves like a full page reload, and the overall feel is more like a desktop app than a modern web app.
Thesis: Use htmx’s hx‑boost to Swap Only What Changes
htmx’s hx‑boost attribute turns ordinary <a> and <form> elements into AJAX requests that return a fragment of the page. The client then swaps the <body> innerHTML with the response, preserving the server‑rendered architecture while giving the user a smooth, SPA‑like experience. No custom JavaScript is required; the server can continue to output full HTML, and the client merges it intelligently.
How hx‑boost Works Under the Hood
- When the user clicks a boosted link or submits a boosted form, htmx intercepts the event.
- The request is sent with
HX-Request: trueandHX-Boosted: trueheaders. - The server receives the request, renders the target page, and returns the full HTML.
- htmx parses the response, extracts the
<body>content, and replaces the current page’s<body>innerHTML. - The browser’s URL and history are updated to match the new page, so the back button works as expected.
Because the entire page is returned, the server can keep the same rendering logic. The only extra effort is ensuring that the page can be safely merged into an existing <body> without duplicating scripts or event handlers.
Concrete Example: Flask + htmx
Below is a minimal Flask app that demonstrates boosting a list page and a detail view. The same templates are used for full navigation and for boosted requests.
# app.py
from flask import Flask, render_template, request
app = Flask(__name__)
# Dummy data
ITEMS = [
{"id": 1, "title": "First Item", "body": "This is the first item."},
{"id": 2, "title": "Second Item", "body": "This is the second item."},
]
@app.route('/')
@app.route('/items')
def list_items():
return render_template('list.html', items=ITEMS)
@app.route('/items/<int:item_id>')
def show_item(item_id):
item = next((i for i in ITEMS if i["id"] == item_id), None)
return render_template('detail.html', item=item)
if __name__ == '__main__':
app.run(debug=True)
Template list.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Items</title>
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
</head>
<body>
<h1>Item List</h1>
<ul>
{% for item in items %}
<li>
<a href="/items/{{ item.id }}" hx-boost>{{ item.title }}</a>
</li>
{% endfor %}
</ul>
</body>
</html>
Template detail.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ item.title }}</title>
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
</head>
<body>
<h1>{{ item.title }}</h1>
<p>{{ item.body }}</p>
<a href="/items" hx-boost>Back to list</a>
</body>
</html>
When you run python app.py and visit http://localhost:5000, clicking a link will swap the <body> without a full reload. The URL updates, the back button works, and the page feels responsive.
Trade‑Offs and Limitations
- Server‑Side Rendering Required: The server must be able to render the page independently. If the server is slow, the boosted request will still download the entire HTML, so the perceived benefit disappears.
- Duplicate Scripts: If the response contains
<script>tags that were already executed on the original page, they will run again unless you guard against it (e.g., usehx-swap-oobor remove scripts before rendering). - Redirects Not Handled: A boosted request that receives a
303 See Otheror302 Foundwill still attempt to merge the response, potentially causing duplicated content. The server should return the final fragment directly. - Forms with Complex Client Logic: If a form relies on JavaScript validation or dynamic UI updates, boosting it may bypass that logic. The form should be designed to work both with and without htmx.
- No WebSocket Support: hx‑boost only enhances navigation; it does not provide real‑time updates.
Verification Checklist
- Open the dev tools and watch the Network tab when clicking a boosted link. The request should have
HX-Boosted: trueand the response status should be200 OKwith a body that matches the target page. - Check that the URL in the address bar changes to the target path and that the back button restores the previous state.
- Search for duplicate
<script>tags in the final DOM. If you see duplicates, adjust the server template to exclude scripts on boosted requests. - Disable JavaScript in the browser and confirm that the link still works as a normal navigation, ensuring graceful degradation.
Actionable Takeaway
Start small: pick one list page, add hx-boost to its links, and measure the perceived loading time and network traffic. If the server can render the fragment quickly, you’ll get instant feedback and can iterate. Once the pattern is stable, extend the boost to forms and detail views. Remember to guard against duplicate scripts and to handle redirects server‑side. With minimal changes to your existing server‑rendered stack, htmx’s hx‑boost can give your users a smoother, SPA‑like experience without the overhead of a full front‑end framework.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.