Conditionally Preventing HX-Boost In HTMX Using An Extension
In an HTMX application, the built-in hx-boost
attribute tells HTMX to intercept all form and anchor interactions and "AJAX'ify" them. That is, prevent the default browser behavior and re-implement it using AJAX and the History
API in order to side-step a full page-load. This is a neat feature; but, it doesn't have any hooks (that I can find) to conditionally prevent hx-boost
from acting on a given link. Such a hook would be necessary when integrating content that comes out of a content management system (CMS). To try and add such a hook, I've created my first HTMX extension, prevent-boost
, that can conditionally prevent boosting based on RegEx patterns and / or filter callbacks.
Consider the following ColdFusion page that's part of an HTMX site:
<cfoutput>
<h1>
About
</h1>
<!---
Imagine that this is coming out of a content management system and we don't have
direct access to the HTML. As such, we can't apply hx-boost or hx-disabled to the
markup directly. Instead, we have to rely on our extension to prevent boosting.
--->
<section data-source="content management system">
<p>
Check out my <a href="demo.cfm">Demo App</a>.
</p>
</section>
</cfoutput>
For this exploration, all of the CFML is hard-coded; but, imagine that the above <section>
element is coming out of a CMS. And, while the overall HTMX site is using hx-boost
, there may be content coming from the CMS that shouldn't be boosted because it links to a non-compatible page. In this case, we don't want demo.cfm
to be boosted.
Normally, we could just add hx-disable
or hx-boost="false"
to the demo link. But, again, we're pretending that we don't have direct access to this markup. As such, we have to implement the "disabling" after the fact.
To do this, we can use an HTMX extension to hook into the htmx:beforeProcessNode
event. This is the event that gets fired right before HTMX examines a node to see if it needs to initialize any data or bind any triggers. We can use this event, within our extension, to conditionally inject the aforementioned hx-disable
and hx-boost="false"
attributes.
Aside: We would only need to inject one of these attributes. But, I'm injecting both for fun; and because I'm using the
hx-boost
attribute more for documentation than for functionality (see the code below).
This interception is done via the extension's onEvent()
handler. Every HTMX event (that can be extended) is passed to this handler, giving our extension an opportunity to augment various control flows within the application's behavior.
In this case, we're going to look for specific htmx:beforeProcessNode
events&mdahs;those that are operating on anchor tags. For each inspected anchor tag, we're going to extract the href
property and compare it to a given set of RegEx patterns or filters. And, if any filter returns false
, we're going to inject the hx-disable
and hx-boost
attributes into said anchor tag.
First, let's look at an abbreviated version of the layout template where the script tags are being installed to see how this HTMX extension can be configured:
<cfoutput>
<!doctype html>
<html lang="en">
<head>
<!--- ... truncated ... --->
<script type="text/javascript" src="/shared/htmx-2.0.4.js"></script>
<script type="text/javascript" src="./prevent-boost.js"></script>
<script type="text/javascript">
// DO NOT BOOST on "/demo.cfm" links.
HxPreventBoost.addPattern( /\/demo\.cfm\b/i );
</script>
</head>
<body
hx-boost="true"
hx-sync="this:replace"
hx-ext="prevent-boost">
<!--- ... truncated ... --->
</body>
</html>
</cfoutput>
In this code, there are several things to notice:
I'm including the
hx-boost
attribute on the body to boost the entire site.I'm including the
hx-ext="prevent-boost"
attribute on the body to tell HTMX that I want to apply my extension to the entire body content.I'm including my extension script tag after I include the HTMX framework.
My extension exposes a global
HxPreventBoost
variable, which exposes a way to configure the extension behavior. And, right after I include my script tag, I'm adding a RegEx pattern to match against the/demo.cfm
link.
We can see that this extension is working by looking at the network activity as we click around the site. For most links, we'll see that only the top level page is requested, bypassing any full-page load. However, when we click on the demo.cfm
link, we can see that all the static assets are loaded. This is because the boosting behavior was prevented and full-page load was executed:

As you can see, most links only load the main content due to hx-boost
. But, the demo.cfm
link, which is intercepted by our extension, causes a full-page load, including all of the linked static assets.
Here's my implementation of the extension:
window.HxPreventBoost = (() => {
// Filter functions are given the node and href and can return an explicit false to
// disable boosting on the given node.
var filters = [];
htmx.defineExtension(
"prevent-boost",
{
onEvent: handleHtmxEvent
}
);
// Return the public API.
return {
addFilter,
addPattern
};
// ---
// PUBLIC METHODS.
// ---
/**
* I add the given filter to the node pre-processing logic. If a filter returns an
* explicit (false), hx-boost will be disabled on the given node.
*/
function addFilter( filter ) {
filters.push( filter );
}
/**
* I add the given regular expression pattern to the node pre-processing logic. If a
* pattern matches against a given HREF property, hx-boost will be disabled on the
* given node.
*
* Note: the HREF value used in the pattern match is the `.href` property value. If you
* need to test against the original href attribute, use a filter and access the
* attribute using the passed-in node.
*/
function addPattern( patternText, patternFlags = "i" ) {
var pattern = ( patternText instanceof RegExp )
? patternText
: new RegExp( patternText, patternFlags )
;
addFilter( ( node, href ) => ( pattern.test( href ) === false ) );
}
// ---
// PRIVATE METHODS.
// ---
/**
* I provide interception logic for the HTMX events.
*/
function handleHtmxEvent( name, event ) {
// There's no way (that I can find) to perform a just-in-time cancellation of the
// hx-boost operation (without actually canceling the default browser behavior as
// well). As such, we're going to prevent boosting at the other end of the life-
// cycle, when HTMX is initializing the HTML content. As HTMX traverses the new
// DOM fragments, looking for nodes to process, we're going to conditionally add
// the hx-boost/hx-disable attributes to nodes that we want to skip. This will
// prevent HTMX from taking further action.
if ( name !== "htmx:beforeProcessNode" ) {
return;
}
var node = event.detail.elt;
// Currently, this extension only handles anchor tags.
if ( node.tagName !== "A" ) {
return;
}
for ( var filter of filters ) {
if ( filter( node, node.href ) === false ) {
return preventBoost( node );
}
}
}
/**
* I apply attributes to the node so that HTMX will ignore interactions.
*/
function preventBoost( node ) {
node.setAttribute( "hx-boost", "false" );
node.setAttribute( "hx-disable", "disabled by prevent-boost extension" );
}
})();
And, if we look at the HTML of the rendered page, we can see that the hx-boost
and hx-disabled
attributes were dynamically injected by my extension:

I know that using hx-boost
is a bit of controversial topic. It definitely makes some things easier and some things harder. Coming from a SPA (single-page application) world, this feels like natural stepping stone. And, for the time being, I'm glad that I can include hx-boost
and still have ways to disable it where and when I need to, even without direct access to the HTML content.
Want to use code from this post? Check out the license.
Reader Comments
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →