NOTE: If you don't know what htmx is, you should check out htmx.org first!

HTMX ESSENTIALS

Before you get started, be sure to include the htmx cdn at the top of your page:

<script src='https://unpkg.com/htmx.org@2.0.4'></script>
There are 4 primary attributes that you will need to do 80% (more like 99%) of your work on your htmx sites. They are:
  1. hx-get - This creates a simple GET ajax request to the url you specify. i.e. hx-get="/examples/1/details"
  2. hx-trigger - This decides how to trigger the ajax request in hx-get. i.e. hx-trigger="click"
  3. hx-target - This uses a css selector to decide where in the dom you will place the returned html from the hx-get. i.e. hx-target="#target-div"
  4. hx-swap - This decides how you will swap the html into the dom, i.e. hx-swap="outerHTML"
Using these 4 attributes allows you to trigger requests and place data from your server anywhere into your page.
Goal: You need to click a specific div on the page to load dynamic data from the server into another div on the page.
This next section of examples will build on each of these 4 essential htmx attributes to get to accomplish that goal.
1. hx-get

#  Click a button to load html
<button hx-get="/htmx/time">
	Click to get server time
</button>

This is the most basic htmx example. We set up a route called /htmx/time that returns the current server time. Then, we have a button that can be clicked to load the route with hx-get.

It assumes a default trigger action of "click", and a default target to place the html return from the get into whichever div was clicked.

Attributes hx-get
Route /htmx/time

#  Click a div to load html
<div hx-get="/htmx/time">
	Click to get server time
</div>
Click to get server time

This is the exact same example as above, but we show that *any* element can be used as a hypermedia control. In this case, we are using a <div> instead of a <button>.

It assumes a default trigger action of "click", and a default target to place the html return from the get into whichever div was clicked.

Attributes hx-get
Route /htmx/time

#  Click to load html (showing defaults)
<div hx-get="/htmx/time"
	 hx-trigger="click"
	 hx-target="this"
	 hx-swap="innerHTML">
	Click to get server time
</div>
Click to get server time

This does exactly the same thing as the examples above. But here we explicitly set the hx-trigger, the hx-target, and the hx-swap method to the exact same as the defaults.

This is just to illustrate that htmx assumes certain default values. In this case, the hx-get on a div assumes a default trigger of click, a default target of this (the same div that is clicked), and a default swap method of innerHTML.

The next several examples will demonstrate what happens when you change these defaults.

Attributes hx-get
hx-trigger
hx-target
hx-swap
Route /htmx/time

#  Click to load html: setting the target
<div hx-get="/htmx/time"
	 hx-target="#hx-get-target">
	 Click this to update target
</div>
<div id="hx-get-target"></div>
Click this to update target

We have now changed the hx-target so that we can send the html returned from hx-get to any element on the page. Use a css selector (in this case the id #hx-get-target) to set the target.

In this case, we set a small div under the clickable div, so when you click it, the target is filled with the html from the /htmx/time route.

Attributes hx-get
hx-target
Route /htmx/time

#  Hover to load html: setting the trigger
<div hx-get="/htmx/time"
	 hx-trigger="mouseenter">
	 Hover over this to get time
</div>
Hover over this to get time

We have now changed the hx-trigger so that we can activate it on a different user event.

In this case, we are using mouseenter to let the user trigger the hx-get when they hover over the div.

Attributes hx-get
hx-trigger
Route /htmx/time

#  Click to load html: setting the swap
<div hx-get="/htmx/time"
	 hx-swap="outerHTML">
	 Click to replace with the server time
</div>
Click to replace with the server time

We have now changed the hx-swap away from the default of innerHTML to outerHTML.

This means that when we get our response, the html completely replaces the element (the div we clicked in this case) with the html from the server.

Attributes hx-get
hx-swap
Route /htmx/time

#  Click to load html to any dom target
<div hx-get="/htmx/time"
	 hx-target="#some-notification-target">
	 Click this to update notifications
</div>
Click this to update notifications

Again, we are using a css selector to set the target, but now we are targeting the element #some-notification-target, which is outside of our original example.

You can see when you click to load the server time, it is now placed in the notification target at the top right of the page.

Attributes hx-get
hx-target
Route /htmx/time

#  Click to load an html form
<div hx-get="/htmx/form">
	Click to load an html form
</div>
Click to load an html form

WARNING: DOESN'T WORK!

Click to load, then try to enter data in the form to see what's wrong.

The reason the form doesn't work is because by default it is loaded into the innerHTML of the div you clicked. The problem is that the div you clicked then wraps the form, and the hx-get attribute on that div will continue to work as expected, and reload the form!

Check out the following examples for a few ways to fix this form example.

Attributes hx-get
Route /htmx/form

#  Click to load an html form *** FIX 1 ***
<div hx-get="/htmx/form"
	 hx-target="#hx-get-form-target">
	Click to load an html form
</div>
<div id="hx-get-form-target"></div>
Click to load an html form

Now we should be able to load the form AND use it correctly.

The reason this works is because we are now loading the form to a separate element than the one that has the click trigger on it.

You can click the div again to *reload* the blank form from the server.

Attributes hx-get
hx-target
Route /htmx/form

#  Click to load an html form *** FIX 2 ***
<div hx-get="/htmx/form"
	 hx-swap="outerHTML">
	Click to load an html form
</div>
Click to load an html form

Again, we should be able to load the form AND use it correctly now.

The reason it works this time is that we have changed the hx-swap default from innerHTML (the default) to outerHTML.

hx-swap="outerHTML" will replace the entire target itself, rather than replace the content inside the div as the innerHTML default does.

Attributes hx-get
hx-swap="outerHTML"
Route /htmx/form
2. hx-trigger

#  Load html with "click" trigger
<div hx-get="/htmx/time"
	 hx-trigger="click">
	Click me to load html
</div>
Click me to load html

Put description here

Attributes hx-get
hx-trigger="click"
Route /htmx/time

#  Load html on click but only once
<div hx-get="/htmx/time"
	 hx-trigger="click once">
	This will load just once
</div>
This will load just once

Loads when input changed

Attributes hx-get
hx-trigger="click once"
Route /htmx/time

#  Load html when input is changed
<div id="select-target">
	Load the html when the input is changed
</div>
<select hx-get="/htmx/time"
	    hx-trigger="change"
	    hx-target="#select-target">
	<option value="">None</option>
	<option value="1">One</option>
	<option value="2">Two</option>
</select>
Load the html when the input is changed

Loads html when the select changes.

Attributes hx-get
hx-trigger="changed"
Route /htmx/time

#  Load html when the div is revealed
<div hx-get="/htmx/time"
	 hx-trigger="revealed">
	 When this gets to the to right place on the page, it will be replaced.
</div>
When this gets to the to right place on the page, it will be replaced.

Loads html when the select changes.

Attributes hx-get
hx-trigger="revealed"
Route /htmx/time

#  Load html when the mouse goes over it
<div hx-get="/htmx/time"
	 hx-trigger="mouseenter">
	 This will be replaced when the mouse goes over it
</div>
This will be replaced when the mouse goes over it

Loads html when the select changes.

Attributes hx-get
hx-trigger="mouseenter"
Route /htmx/time

#  Load html when the mouse leaves a div
<div hx-get="/htmx/time"
	 hx-trigger="mouseleave">
	 This will be replaced when the mouse leaves the div
</div>
This will be replaced when the mouse leaves the div

Mouse leave

Attributes hx-get
hx-trigger="mouseleave"
Route /htmx/time

#  Load html when any key is pressed on an input
<input hx-get="/htmx/time"
	   hx-trigger="keyup"
	   hx-target="#input-keyup-div" />
<div id="input-keyup-div">
	This will get replaced
</div>
This will get replaced

When key pressed

Attributes hx-get
hx-trigger="keyup"
Route /htmx/time

#  Lazy-load html with "load" trigger
<div hx-get="/htmx/time"
	 hx-trigger="load">
	You will never see this.
</div>
You will never see this.

Now, we are changing the default hx-trigger so that instead of "click", it will trigger the hx-get on "load".

The "load" trigger option is triggered when the page loads. This pattern is commonly called lazy loading. Because it happens immediately on load, you will likely not see the original text in the div.

Attributes
Route /htmx/time

#  Load html with the "every" trigger (aka polling)
<div hx-get="/htmx/time"
	 hx-trigger="every 5s">
	Polling every 5 seconds
</div>
Polling every 5 seconds

Put description here

Attributes hx-get
hx-trigger="every 5s"
Route /htmx/time
3. hx-target

#  Put html into a target with an id on a click
<button hx-get="/htmx/time"
		hx-target="#time-target-click">
	Click me
</button>
<div id="time-target-click">
	Replace this target on click
</div>
Replace this target on click
Attributes hx-get
hx-target
Route /htmx/time

#  Put html into a target with a class on a click
<button hx-get="/htmx/time"
		hx-target=".time-target-click">
	Click me
</button>
<div class="time-target-click">
	Replace this target on click
</div>
Replace this target on click
Attributes hx-get
hx-target
Route /htmx/time

#  Put html into a target with the 'closest' css selector
<div>
	Replace this target on click
	<button hx-get="/htmx/time"
			hx-target="closest div">
		Click me
	</button>
</div>
Replace this target on click
Attributes hx-get
hx-target
Route /htmx/time
4. hx-swap

#  Swap data to replace the innerHTML
<button hx-get="/htmx/time"
		hx-target="#swap-target">
	Click to replace html			
</button>
<div id="swap-target">
	This will be replaced
</div>
This will be replaced
Attributes hx-get
hx-target
hx-swap="innerHTML"
Route /htmx/time

#  Swap data to replace the outerHTML
<button hx-get="/htmx/time"
		hx-target="#swap-target"
		hx-swap="outerHTML">
	Click to replace html		
</button>
<div id="swap-target">
	This will be replaced
</div>
This will be replaced
Because we are using hx-swap outerHTML, the #swap-target no longer exists after it has loaded once.
Attributes hx-get
hx-target
hx-swap="outerHTML"
Route /htmx/time

#  Swap data to the target using beforebegin
<button hx-get="/htmx/time"
		hx-target="#swap-target-list"
		hx-swap="beforebegin">
	Click to place the new html before the list		
</button>
<ul id="swap-target-list" style="border: 2px solid gray; padding: 5px;">
	<li>Item 1</li>
	<li>Item 2</li>
	<li>Item 3</li>
</ul>
  • Item 1
  • Item 2
  • Item 3
Because we are using hx-swap outerHTML, the #swap-target no longer exists after it has loaded once.
Attributes hx-get
hx-target
hx-swap="beforebegin"
Route /htmx/time

#  Swap data to the target using afterbegin
<button hx-get="/htmx/time"
		hx-target="#swap-target-list-after"
		hx-swap="afterbegin">
	Click to place the new html at the top of the list	
</button>
<ul id="swap-target-list-after" style="border: 2px solid gray; padding: 5px;">
	<li>Item 1</li>
	<li>Item 2</li>
	<li>Item 3</li>
</ul>
  • Item 1
  • Item 2
  • Item 3
Because we are using hx-swap outerHTML, the #swap-target no longer exists after it has loaded once.
Attributes hx-get
hx-target
hx-swap="afterbegin"
Route /htmx/time

#  Swap data to the target using beforeend
<button hx-get="/htmx/time"
		hx-target="#swap-target-list-bottom"
		hx-swap="beforeend">
	Click to place the new html at the bottom of the list	
</button>
<ul id="swap-target-list-bottom" style="border: 2px solid gray; padding: 5px;">
	<li>Item 1</li>
	<li>Item 2</li>
	<li>Item 3</li>
</ul>
  • Item 1
  • Item 2
  • Item 3
Because we are using hx-swap outerHTML, the #swap-target no longer exists after it has loaded once.
Attributes hx-get
hx-target
hx-swap="beforeend"
Route /htmx/time

#  Swap data to the target using afterend
<button hx-get="/htmx/time"
		hx-target="#swap-target-list-under"
		hx-swap="afterend">
	Click to place the new html underneath the list	
</button>
<ul id="swap-target-list-under" style="border: 2px solid gray; padding: 5px;">
	<li>Item 1</li>
	<li>Item 2</li>
	<li>Item 3</li>
</ul>
  • Item 1
  • Item 2
  • Item 3
Because we are using hx-swap outerHTML, the #swap-target no longer exists after it has loaded once.
Attributes hx-get
hx-target
hx-swap="afterend"
Route /htmx/time

#  Delete a targeted item after loading
<button hx-get="/htmx/time"
		hx-target="#swap-target-list-delete"
		hx-swap="delete">
	Click to delete the list after the html loads
</button>
<ul id="swap-target-list-delete" style="border: 2px solid gray; padding: 5px;">
	<li>Item 1</li>
	<li>Item 2</li>
	<li>Item 3</li>
</ul>
  • Item 1
  • Item 2
  • Item 3
Because we are using hx-swap outerHTML, the #swap-target no longer exists after it has loaded once.
Attributes hx-get
hx-target
hx-swap="delete"
Route /htmx/time

#  Do nothing on the swap
<button hx-get="/htmx/time"
		hx-target="#swap-target-list-none"
		hx-swap="none">
	Click to not change the the list after the html loads
</button>
<ul id="swap-target-list-none" style="border: 2px solid gray; padding: 5px;">
	<li>Item 1</li>
	<li>Item 2</li>
	<li>Item 3</li>
</ul>
  • Item 1
  • Item 2
  • Item 3
Because we are using hx-swap outerHTML, the #swap-target no longer exists after it has loaded once.
Attributes hx-get
hx-target
hx-swap="none"
Route /htmx/time
Essentials examples

#  Dynamic Modal
<style>
    dialog {
        display: flex;
        opacity: 0;
        pointer-events: none;
        transform: scale(0.9);
    }
    dialog[open] {
        display: flex;
        opacity: 1;
        transform: scale(1);
        pointer-events: inherit;
        transition: opacity 0.1s ease-in,
         			transform 0.1s ease-in;
    }
    dialog::backdrop {
        background: rgba(0,0,0,0.3);
        backdrop-filter: blur(3px);
    }
</style>

<button hx-get="/examples/modal-form"
        hx-trigger="mouseenter"
        hx-target="#modal-content"
        onclick="window.my_modal.showModal()">
    Open Modal
</button>

<dialog id="my_modal" 
		class="rounded-lg shadow-lg w-3/4">
    <div class="w-full">
        <div class="font-bold text-black text-lg border-b p-4">
            <div class="float-right cursor-pointer text-gray-400 hover:text-gray-600 transition" 
            	 onclick="window.my_modal.close()">
                <svg xmlns="http://www.w3.org/2000/svg" 
                	 fill="none" 
                	 viewBox="0 0 24 24" 
                	 stroke-width="1.5" 
                	 stroke="currentColor" 
                	 class="size-6">
                  	<path stroke-linecap="round" 
                  		  stroke-linejoin="round"d="M6 18 18 6M6 6l12 12" />
                </svg>
            </div>
            Dynamic Modal Example
        </div>

        <div id="modal-content">
            <div class="p-8 text-center text-xl text-gray-400">
                Loading....
            </div>
        </div>
    </div>
</dialog>
Dynamic Modal Example
Attributes
Route
EXTENSIONS

HTMX extensions add additional functionality to the core library. Extensions are loaded via CDN or npm and enabled using the hx-ext attribute. They can enhance HTMX with features like head tag support, client-side templates, and more.

head-support

To use the head-support extension, first include the script and enable it on your page:

<script src="https://cdn.jsdelivr.net/npm/htmx-ext-head-support@2.0.2"></script>

<body hx-ext="head-support">

The server responses contain complete HTML documents with updated titles:

<html>
<head>
    <title hx-head="re-eval">🟢 Active - hype cp | Hypermedia Copy & Paste</title>
</head>
</html>
<html>
<head>
    <title hx-head="re-eval">🔴 Inactive - hype cp | Hypermedia Copy & Paste</title>
</head>
</html>

#  Updating the page title
<button hx-get="/htmx/head-support/title-active"
        hx-swap="none">
    Set Active Title
</button>

<button hx-get="/htmx/head-support/title-inactive"
        hx-swap="none">
    Set Inactive Title
</button>

The head-support extension allows HTMX to process <head> tags in responses, enabling dynamic updates to the page title, meta tags, CSS, and other head elements.

In this example, clicking the buttons sends requests that return only a <head> tag with a new <title>. The hx-head="re-eval" attribute on the title tag tells the extension to replace the existing title with the new one.

Using hx-swap="none" prevents any content changes while the head-support extension processes the <head> tag and updates only the page title.

Attributes hx-ext="head-support"
hx-get
hx-swap="none"
hx-head="re-eval"
Route /htmx/head-support/title-active
/htmx/head-support/title-inactive

#  Updating CSS styles
<button hx-get="/htmx/head-support/css-blue"
        hx-swap="none">
    Blue Theme
</button>

<button hx-get="/htmx/head-support/css-red"
        hx-swap="none">
    Red Theme
</button>

<div id="css-demo" class="p-4 border rounded">
    <h3>CSS Demo</h3>
    <p>Click the buttons above to change the head tag for the CSS of this box.</p>
</div>

CSS Demo

Click the buttons above to change the head tag for the CSS of this box.

This example demonstrates how to dynamically update CSS styles using the head-support extension.

The responses contain <style> tags with hx-head="re-eval" that replace existing styles with the same ID. The extension processes the <head> tag and updates the page's CSS.

Using hx-swap="none" prevents any content changes while only the styling gets updated.

Attributes hx-ext="head-support"
hx-get
hx-swap="none"
hx-head="re-eval"
Route /htmx/head-support/css-blue
/htmx/head-support/css-red

The server responses contain style tags with the same ID for replacement:

<html>
<head>
    <style id="demo-styles" hx-head="re-eval">
        #css-demo {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: 3px solid #4c51bf;
            box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
        }
        #css-demo h3 {
            color: #e6fffa;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }
    </style>
</head>
</html>
<html>
<head>
    <style id="demo-styles" hx-head="re-eval">
        #css-demo {
            background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
            color: white;
            border: 3px solid #e53e3e;
            box-shadow: 0 10px 25px rgba(245, 87, 108, 0.3);
        }
        #css-demo h3 {
            color: #fed7d7;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }
    </style>
</head>
</html>
preload

The preload extension allows you to load HTML fragments into your browser's cache before they are requested by the user, making pages appear to load nearly instantaneously. It's enabled by default on this page.


#  Preloading content on mousedown
<button hx-get="/htmx/preload/content" 
        hx-target="#preload-demo"
        preload>
    Hover and click me (content preloads on mousedown)
</button>

<div id="preload-demo" class="p-4 border rounded mt-4">
    <p class="text-gray-500">Content will load here...</p>
</div>

Content will load here...

The preload extension begins loading content when the user starts clicking (mousedown event). This gives your server a 100-200ms head start, making the response appear nearly instantaneous.

Simply add the preload attribute to any hx-get element or <a href> link. The extension automatically handles the preloading in the background.

Note: Preloading only works with GET requests. POST, PUT, and DELETE requests cannot be preloaded for security reasons.

Attributes preload
hx-get
hx-target
Route /htmx/preload/content

#  Preloading form responses
<form hx-get="/htmx/preload/form-response" 
      hx-target="#form-demo"
      preload>
    
    <div class="mb-4">
        <label class="block text-sm font-medium mb-2">Select Plan:</label>
        <input type="radio" name="plan" value="basic" class="mr-2"> Basic
        <input type="radio" name="plan" value="pro" class="mr-2"> Pro
        <input type="radio" name="plan" value="enterprise" class="mr-2"> Enterprise
    </div>
    
    <div class="mb-4">
        <label class="block text-sm font-medium mb-2">Features:</label>
        <input type="checkbox" name="features[]" value="video" class="mr-2"> Video
        <input type="checkbox" name="features[]" value="audio" class="mr-2"> Audio
        <input type="checkbox" name="features[]" value="support" class="mr-2"> Support
    </div>
    
    <div class="mb-4">
        <label class="block text-sm font-medium mb-2">Region:</label>
        <select name="region" class="border rounded px-3 py-2">
            <option value="us">United States</option>
            <option value="eu">Europe</option>
            <option value="asia">Asia</option>
        </select>
    </div>
    
    <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
        Submit Form (Content Preloaded!)
    </button>
</form>

<div id="form-demo" class="p-4 border rounded mt-4">
    <p class="text-gray-500">Form responses will appear here...</p>
</div>
Basic Pro Enterprise
Video Audio Support

Form responses will appear here...

The preload extension can preload form responses when users interact with form elements like radio buttons, checkboxes, and select dropdowns.

When you hover over or interact with form elements, the extension preloads the server response as if the form was submitted with those values. This makes form interactions appear nearly instantaneous.

Try it: Hover over different radio buttons, checkboxes, or change the select dropdown to see responses preload in the background.

Attributes preload
hx-get
hx-target
form elements
Route /htmx/preload/form-response
idiomorph

The idiomorph extension provides DOM morphing capabilities, allowing HTMX to smoothly transition between different states by reusing existing DOM nodes when possible. This creates much smoother animations and preserves element state.


#  Simple morphing example
<button hx-get="/htmx/idiomorph/simple" 
        hx-target="#simple-morph"
        hx-swap="morph">
    Load Simple Content
</button>

<div id="simple-morph">
    <p>Click the button to see simple morphing in action!</p>
</div>

Click the button to see simple morphing in action!

This simple example demonstrates basic DOM morphing. The button loads new content using hx-swap="morph", which smoothly transitions the entire element.

Attributes hx-swap="morph"
hx-get
hx-target
Route /htmx/idiomorph/simple

#  DOM morphing with smooth transitions
<button hx-get="/htmx/idiomorph/content-1" 
        hx-target="#morph-demo"
        hx-swap="morph:innerHTML">
    Morph Swap 1
</button>

<button hx-get="/htmx/idiomorph/content-2" 
        hx-target="#morph-demo"
        hx-swap="innerHTML">
    Regular Swap 2
</button>

<button hx-get="/htmx/idiomorph/content-3" 
        hx-target="#morph-demo"
        hx-swap="morph:innerHTML">
    Morph Swap 3
</button>

<div id="morph-demo" class="p-6 border-2 border-purple-200 rounded-lg mt-4 bg-purple-50">
    <h3 class="text-purple-800 font-semibold mb-2">Initial Content</h3>
    <p class="text-purple-700">This is the starting content. Click the buttons above to see smooth DOM morphing in action!</p>
    <div class="mt-3 p-2 bg-purple-100 rounded">
        <span class="text-sm text-purple-600">Element with preserved state</span>
    </div>
</div>

Initial Content

This is the starting content. Click the buttons above to see smooth DOM morphing in action!

Element with preserved state

The idiomorph extension uses DOM morphing to smoothly transition between different HTML states. Instead of completely replacing elements, it intelligently reuses existing DOM nodes when possible.

Using hx-swap="morph:innerHTML" tells HTMX to morph only the inner children of the target element, leaving the container itself untouched. Compare this with hx-swap="innerHTML" which completely replaces the content.

Try it: Click "Morph Swap" buttons to see smooth transitions, then click "Regular Swap 2" to see the difference with traditional swapping. Notice how morphing preserves element state while regular swapping creates a jarring replacement.

Attributes hx-swap="morph:innerHTML"
hx-get
hx-target
Route /htmx/idiomorph/content-1
/htmx/idiomorph/content-2
/htmx/idiomorph/content-3
response-targets

The response-targets extension allows you to specify different target elements for different HTTP response codes. This enables better error handling and user experience by routing responses to appropriate UI locations.

To use the response-targets extension, first include the script and enable it on your page:

<script src="https://cdn.jsdelivr.net/npm/htmx-ext-response-targets@2.0.2"></script>

<div hx-ext="response-targets">

#  Basic response targeting example
<div hx-ext="response-targets">
    <div id="response-div" class="p-4 border rounded bg-gray-50">
        <p class="text-gray-600">Response will appear here...</p>
    </div>
    
    <button hx-get="/htmx/response-targets/register"
            hx-target="#response-div"
            hx-target-5*="#serious-errors"
            hx-target-404="#not-found"
            class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
        Register!
    </button>
    
    <div id="serious-errors" class="p-4 border rounded bg-red-50 mt-4">
        <p class="text-red-600">Server errors will appear here...</p>
    </div>
    
    <div id="not-found" class="p-4 border rounded bg-yellow-50 mt-4">
        <p class="text-red-600">404 errors will appear here...</p>
    </div>
</div>

Response will appear here...

Server errors will appear here...

404 errors will appear here...

The response-targets extension allows you to route different HTTP response codes to different UI elements. In this example:

  • 200 responses go to #response-div (normal success)
  • 5xx responses go to #serious-errors (server errors)
  • 404 responses go to #not-found (not found errors)

Random Response Demo: The server randomly returns different status codes to demonstrate the extension in action:

  • 50% chance of 200 success (green success message)
  • 30% chance of 500 server error (red error message)
  • 20% chance of 404 not found (yellow 404 message)

Click the button multiple times to see how different responses automatically get routed to their appropriate targets.

Attributes hx-ext="response-targets"
hx-get
hx-target
hx-target-5*
hx-target-404
Route /htmx/response-targets/register

#  Using hx-target-error for all error codes
<div hx-ext="response-targets">
    <div id="response-div-2" class="p-4 border rounded bg-gray-50">
        <p class="text-gray-600">Response will appear here...</p>
    </div>
    
    <button hx-get="/htmx/response-targets/register"
            hx-target="#response-div-2"
            hx-target-error="#any-errors"
            class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">
        Register (All Errors)
    </button>
    
    <div id="any-errors" class="p-4 border rounded bg-red-50 mt-4">
        <p class="text-red-600">All 4xx and 5xx errors will appear here...</p>
    </div>
</div>

Response will appear here...

All 4xx and 5xx errors will appear here...

Instead of handling different error codes separately, you can use hx-target-error to route all 4xx and 5xx responses to a single error target.

This is useful when you want to display all errors in a unified error area rather than having separate locations for different types of errors.

Attributes hx-ext="response-targets"
hx-get
hx-target
hx-target-error
Route /htmx/response-targets/register

#  Testing 404 response targeting
<div hx-ext="response-targets">
    <div id="response-div-3" class="p-4 border rounded bg-gray-50">
        <p class="text-gray-600">Response will appear here...</p>
    </div>
    
    <button hx-get="/htmx/response-targets/not-found"
            hx-target="#response-div-3"
            hx-target-404="#not-found-2"
            class="bg-purple-500 text-white px-4 py-2 rounded hover:bg-purple-600">
        Test 404 Response
    </button>
    
    <div id="not-found-2" class="p-4 border rounded bg-yellow-50 mt-4">
        <p class="text-yellow-600">404 errors will appear here...</p>
    </div>
</div>

Response will appear here...

404 errors will appear here...

This example specifically tests 404 response targeting. The button makes a request to a route that returns a 404 status, demonstrating how the extension routes error responses to the appropriate target.

Note: The hx-target-404 attribute will catch 404 responses and route them to the specified target, while successful responses (200) go to the default hx-target.

Attributes hx-ext="response-targets"
hx-get
hx-target
hx-target-404
Route /htmx/response-targets/not-found