Skip to content
Advertisement

Single-page-application with HTMX, URL browsing history and manual reloading of a page

I have an application with fixed header and footer (it is important that they are not reloaded when we navigate through pages). I use HTMX. The attribute hx-push-url allows to have browsing history through the pages: we can use the “back” and “forward” buttons, and it works as expected:

index.html:

<style>
#header { background-color: yellow; height: 20%; }
#container { background-color: gray; height: 60%; }
#footer { background-color: yellow; height: 20%; }
</style>
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
<div id="header">This is the header</div>
<div id="container">
This is the main container
<button hx-get="newpage1" hx-target="#container" hx-swap="outerHTML" hx-push-url="true">Go to newpage1</button>
</div>
<div id="footer">This is the footer</div>

newpage1:

<div id="container">Hello
<button hx-get="newpage2" hx-target="#container" hx-swap="outerHTML" hx-push-url="true">Go to newpage2</button>
</div>

newpage2:

<div id="container">Test</div>

After having clicked on “Go to newpage1” button, the container div is replaced by the content of newpage1 as expected and the browser URL is now http://127.0.0.1/newpage1.

Problem: if we enter this URL directly in the browser, we won’t have the full page, but only the container <div id="container">...</div>.

What is the common HTMX solution to this problem when we build a single page application?

Answer

On the server side, check the Request Headers for the HX-Request header, and if it exists, then just send your partial, otherwise send the full page.

If you are using Django on the server side, this method is explained step by step in this part of a blog article:

The problem with the above is that if a user manually reloads the token or the support page, he will only end up with the HTML fragment instead of the whole HTML page. The solution, on the Django side, is to render 2 different templates depending on whether the request is coming from htmx or not. Here is how you could do it. In your views.py you need to check whether the HTTP_HX_REQUEST header was passed in the request. If it was, it means this is a request from htmx and in that case you can show the HTML fragment only. If it was not, you need to render the full page.

Advertisement