Lazy Load
# Lazy Load
<script>
server.get("/demo", () => `
<div hx-get="/weather" hx-trigger="load" hx-swap="outerHTML">
<div class="size-full flex items-center justify-center gap-2 text-sm starting:opacity-0 transition">
<span class="inline-block size-4 border-2 border-neutral-200 dark:border-neutral-700 border-t-neutral-400 dark:border-t-neutral-400 rounded-full animate-spin"></span>
Loading forecast…
</div>
</div>`);
server.get("/weather", () => ({ delay: 300, body: `
<div class="size-full">
<div class="starting:opacity-0 starting:translate-y-1 transition flex items-center gap-2 mb-4">
<span class="text-xl">☁</span>
<span class="text-sm font-semibold text-neutral-700 dark:text-neutral-200">5-Day Forecast</span>
</div>
<div class="grid grid-cols-5 gap-1 text-center">
<div class="flex flex-col items-center gap-1 py-3 px-2 rounded-md starting:opacity-0 starting:translate-y-1 hover:bg-neutral-50 dark:hover:bg-neutral-850" style="transition:opacity 300ms ease-out 50ms,transform 300ms ease-out 50ms,background-color 150ms ease 0ms"><span class="text-xs uppercase tracking-wide text-neutral-500 dark:text-neutral-400">Mon</span><span class="text-lg font-semibold text-neutral-800 dark:text-neutral-100">72°</span><span class="text-xs text-neutral-500 dark:text-neutral-400">Sunny</span></div>
<div class="flex flex-col items-center gap-1 py-3 px-2 rounded-md starting:opacity-0 starting:translate-y-1 hover:bg-neutral-50 dark:hover:bg-neutral-850" style="transition:opacity 300ms ease-out 100ms,transform 300ms ease-out 100ms,background-color 150ms ease 0ms"><span class="text-xs uppercase tracking-wide text-neutral-500 dark:text-neutral-400">Tue</span><span class="text-lg font-semibold text-neutral-800 dark:text-neutral-100">68°</span><span class="text-xs text-neutral-500 dark:text-neutral-400">Cloudy</span></div>
<div class="flex flex-col items-center gap-1 py-3 px-2 rounded-md starting:opacity-0 starting:translate-y-1 hover:bg-neutral-50 dark:hover:bg-neutral-850" style="transition:opacity 300ms ease-out 150ms,transform 300ms ease-out 150ms,background-color 150ms ease 0ms"><span class="text-xs uppercase tracking-wide text-neutral-500 dark:text-neutral-400">Wed</span><span class="text-lg font-semibold text-neutral-800 dark:text-neutral-100">65°</span><span class="text-xs text-neutral-500 dark:text-neutral-400">Rain</span></div>
<div class="flex flex-col items-center gap-1 py-3 px-2 rounded-md starting:opacity-0 starting:translate-y-1 hover:bg-neutral-50 dark:hover:bg-neutral-850" style="transition:opacity 300ms ease-out 200ms,transform 300ms ease-out 200ms,background-color 150ms ease 0ms"><span class="text-xs uppercase tracking-wide text-neutral-500 dark:text-neutral-400">Thu</span><span class="text-lg font-semibold text-neutral-800 dark:text-neutral-100">70°</span><span class="text-xs text-neutral-500 dark:text-neutral-400">Partly cloudy</span></div>
<div class="flex flex-col items-center gap-1 py-3 px-2 rounded-md starting:opacity-0 starting:translate-y-1 hover:bg-neutral-50 dark:hover:bg-neutral-850" style="transition:opacity 300ms ease-out 250ms,transform 300ms ease-out 250ms,background-color 150ms ease 0ms"><span class="text-xs uppercase tracking-wide text-neutral-500 dark:text-neutral-400">Fri</span><span class="text-lg font-semibold text-neutral-800 dark:text-neutral-100">74°</span><span class="text-xs text-neutral-500 dark:text-neutral-400">Sunny</span></div>
</div>
</div>` }));
</script>
<div hx-get="/demo" hx-trigger="load" class="not-prose demo-container flex justify-center min-h-[218px]"></div>
## Basic usage
On the client, put a placeholder where the content will go.
```html
<div hx-get="/weather" hx-trigger="load">
Loading...
</div>
```
- [`hx-get`](https://four.htmx.org/reference/attributes/hx-get)=`/weather` might query a database or call an external API.
- [`hx-trigger`](https://four.htmx.org/reference/attributes/hx-trigger)=[`"load"`](https://four.htmx.org/reference/attributes/hx-trigger#load) fires the request as soon as the element enters the DOM.
On the server, respond with the HTML content. htmx swaps it into the placeholder when it arrives.
```html
<div class="forecast">
<h3>5-Day Forecast</h3>
<p>Monday: 72° Sunny</p>
<p>Tuesday: 68° Cloudy</p>
</div>
```
## Multiple sections
Each placeholder loads independently. Your page renders fast even if several sections are slow:
```html
<div hx-get="/dashboard/sales" hx-trigger="load">
Loading sales...
</div>
<div hx-get="/dashboard/analytics" hx-trigger="load">
Loading analytics...
</div>
<div hx-get="/dashboard/notifications" hx-trigger="load">
Loading notifications...
</div>
```
All three requests fire in parallel when the page loads.
## Notes
### Layout shift
When the response swaps in, it can push content below it down. This is known as [Cumulative Layout Shift (CLS)](https://web.dev/articles/cls) and affects your [Lighthouse](https://developer.chrome.com/docs/lighthouse) score.
Reserve space with `min-height`:
```html
<div hx-get="/weather" hx-trigger="load" style="min-height: 200px">
Loading...
</div>
```
The demo above does exactly this.
### Infinite loops
Be careful when including `hx-trigger="load"` in the server response.
```
load → GET /weather → response has hx-trigger="load" → load → GET /weather → …
```
If the response requests the same endpoint, it can create an infinite loop. Load content after the page renders
On the client, put a placeholder where the content will go.
<div hx-get="/weather" hx-trigger="load">
Loading...
</div>
hx-get=/weather might query a database or call an external API.
hx-trigger="load" fires the request as soon as the element enters the DOM.
On the server, respond with the HTML content. htmx swaps it into the placeholder when it arrives.
<div class="forecast">
<h3>5-Day Forecast</h3>
<p>Monday: 72° Sunny</p>
<p>Tuesday: 68° Cloudy</p>
</div>
Each placeholder loads independently. Your page renders fast even if several sections are slow:
<div hx-get="/dashboard/sales" hx-trigger="load">
Loading sales...
</div>
<div hx-get="/dashboard/analytics" hx-trigger="load">
Loading analytics...
</div>
<div hx-get="/dashboard/notifications" hx-trigger="load">
Loading notifications...
</div>
All three requests fire in parallel when the page loads.
When the response swaps in, it can push content below it down. This is known as Cumulative Layout Shift (CLS) and affects your Lighthouse score.
Reserve space with min-height:
<div hx-get="/weather" hx-trigger="load" style="min-height: 200px">
Loading...
</div>
The demo above does exactly this.
Be careful when including hx-trigger="load" in the server response.
load → GET /weather → response has hx-trigger="load" → load → GET /weather → …
If the response requests the same endpoint, it can create an infinite loop.