HTMX extension for optimistic UI updates with automatic rollback on errors
npm install hx-optimisticAn htmx extension for optimistic UI updates with automatic rollback on errors. Combine it with speculation rules (or the htmx preload extension) and the View Transitions API for truly appโlike experience with minimal JavaScript. We love JavaScript, but use it like a spice: a pinch delights, too much overwhelms.
- ๐ฏ Optimistic Updates - Immediate UI feedback while requests are processing
- ๐ Automatic Rollback - Intelligent revert to original state on errors
- ๐ Input Interpolation - Dynamic templates with ${this.value}, ${textarea}, ${data:key} helpers
- ๐จ Template Support - Rich HTML templates for loading and error states
- โ ๏ธ Developer Warnings - Console warnings for unsupported patterns
- ๐ซ No CSS Required - You control all styling through provided class names
- ๐ฆ Tiny - Only 18.2KB uncompressed, 8.4KB minified, 3.0KB gzipped
- ๐ง Highly Configurable - Fine-tune behavior per element
Via CDN (jsDelivr, pinned major):
``html`
Alternative (unpkg, latest v1):
`html`
Compatibility: Works with htmx 1.9+ and 2.x.
Via NPM:
`bash`
npm install hx-optimistic
Enable the extension and add optimistic behavior to any HTMX element:
`html`
hx-post="/api/like"
hx-target="this"
hx-swap="outerHTML"
data-optimistic='{"values":{"textContent":"โค๏ธ Liked!","className":"btn liked"},"errorMessage":"Failed to like"}'
>
๐ค Like
- Use data-optimistic to declare behavior per elementvalues
- Choose between (simple property changes) and template (full HTML)innerHTML
- Automatic snapshot and revert: , className, attributes, and datasethx-target
- Token-based concurrency prevents stale errors from overwriting newer states
- Target resolution via or config target chains: closest, find, next, previouserrorMessage
- Errors via or errorTemplate with errorMode and delay
- Interpolation supports safe patterns only; avoid arbitrary JS expressions
| Artifact | Size |
|---------|------|
| Unminified (hx-optimistic.js) | 18.2 KB |hx-optimistic.min.js
| Minified () | 8.4 KB |
| Minified + gzip | 3.0 KB |
Values - Perfect for simple optimistic updates:
`html`
data-count="42"
data-liked="false"
hx-post="/api/like"
hx-target="this"
hx-swap="outerHTML"
data-optimistic='{
"values": {
"textContent": "โค๏ธ Liked! (was ${data:count})",
"className": "btn liked",
"data-liked": "true"
}
}'>
๐ค Like (42)
Templates - Ideal for complex optimistic UI changes:
`html`
data-optimistic='{
"template": "You: ${textarea}",
"errorTemplate": "โ Comment failed to post"
}'>
Dynamic content using ${...} syntax with powerful helpers:
`html`
data-optimistic='{"template":"Posting: ${textarea}"}'>
All ${...} patterns supported in templates and values:
| Pattern | Description | Example |
|---------|-------------|---------|
| ${this.value} | Element's input value | "Saving: ${this.value}" |${this.textContent}
| | Element's text content | "Was: ${this.textContent}" |${this.dataset.key}
| | Data attribute via dataset | "ID: ${this.dataset.userId}" |${textarea}
| | First textarea in form | "Comment: ${textarea}" |${email}
| | First email input | "Email: ${email}" |${data:key}
| | Data attribute shorthand | "Count: ${data:count}" |${attr:name}
| | Any HTML attribute | "ID: ${attr:id}" |${contextKey}
| | Value from config.context (templates only) | "Hello, ${username}" |${status}
| | HTTP status (errors only) | "Error ${status}" |${statusText}
| | HTTP status text (errors only) | "Error: ${statusText}" |${error}
| | Error message (errors only) | "Failed: ${error}" |
Form Field Helpers:
- ${textarea}, ${email}, ${password}, ${text}, ${url}, ${tel}, ${search}${fieldName}
- - Any field with name="fieldName"
Complete configuration reference for data-optimistic:
, className, all attributes, and the element dataset are automatically captured and restored on revert; no configuration is required.$3
`javascript
{
// Simple property updates
"values": {
"textContent": "Loading...",
"className": "btn loading"
},
// Rich HTML templates
"template": "#loading-template", // Or inline HTML
"target": "closest .card", // Different target for optimistic update
"swap": "beforeend", // Append instead of replace
"class": "my-optimistic" // Optional custom class applied during optimistic state
}
`swap supports beforeend and afterbegin. If omitted, content is replaced.$3
`javascript
{
"errorMessage": "Request failed",
"errorTemplate": "Error ${status}: ${statusText}",
"errorMode": "append", // "replace" (default) or "append"
"delay": 2000 // Auto-revert delay in ms
}
`$3
Provide additional variables for template interpolation:`json
{
"template": "Hello, ${username}",
"context": { "username": "Alice" }
}
`Full example:
`html
Alex
Status: ๐ด Offline
hx-post="/api/status"
hx-target="closest .profile-card"
hx-swap="outerHTML"
hx-ext="optimistic"
data-optimistic='{"template":"#profile-next","errorTemplate":"#profile-error","errorMode":"append","context":{"username":"Alex","nextStatus":"Online","nextIcon":"๐ข"}}'
>
Change Status
${username}
Status: ${nextIcon} ${nextStatus}
โ ${error}
`๐จ CSS Classes
This library does not include any CSS. These classes are applied so you can style them as you wish:
-
hx-optimistic: applied during the optimistic update
- hx-optimistic-error: applied when an error is shown
- hx-optimistic-reverting: applied while reverting to the snapshot
- hx-optimistic-error-message: wrapper added when errorMode is "append"
- hx-optimistic-pending: may be applied to