# Progress Bar

<div id="demo-content" class="not-prose demo-container flex flex-col justify-center min-h-[191px]"></div>

## Basic usage

On the client, a button starts the job.

```html
<button hx-post="/start" hx-swap="outerMorph">
  Start Job
</button>
```

On the server, respond with a container that polls for progress:

```html
<div hx-trigger="every 400ms"
     hx-get="/job/progress"
     hx-swap="outerMorph">
  ...progress bar...
</div>
```

- [`hx-trigger`](https://four.htmx.org/reference/attributes/hx-trigger)=[`"every 400ms"`](https://four.htmx.org/reference/attributes/hx-trigger#polling) polls the server on an interval.
- [`outerMorph`](https://four.htmx.org/reference/attributes/hx-swap#outermorph) morphs the element in place, so CSS transitions on `transform` animate smoothly.

Each poll returns updated progress. When done, the server responds with [HTTP 286](https://en.wikipedia.org/wiki/86_(term)) to stop polling.

## Notes

### Use `transform` instead of `width`

Animating `width` causes [layout recalculation](https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing). Use `transform: scaleX()` instead ([GPU-composited](https://web.dev/articles/stick-to-compositor-only-properties-and-manage-layer-count), no layout thrashing):

```css
.bar {
  width: 100%;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 400ms ease-in-out;
}
```

The server returns `style="transform: scaleX(0.65)"` instead of `style="width: 65%"`. Same look, no layout thrashing. The demo above uses this approach.