# htmx Small Business Kit: Deploy and Use
November 1, 2026. Companion to [htmx for small and medium businesses](https://jwatte.com/blog/htmx-small-business-guide/).
## How to use this kit
Give this to the developer who maintains your existing website. Pick one annoying interaction, preserve its ordinary page URL, and agree what success looks like before changing the rest. The examples cover htmx 2.0.11 and 4.0.0. They are small interface patterns, not complete account, permission or database systems. Replace the illustrative paths with your application routes and keep authentication, authorization, CSRF checking and output escaping on the server.
We exercised all nine interaction patterns below in a headless browser against both exact releases on November 1, 2026. The tests checked real HTML swaps, form errors and success, cancellation and confirmation of deletion, and the CSP shown here. The framework examples are documentation based integration guidance; they were not installed as nine separate framework applications.
## A one page plan
| Decision | Write your answer |
|---|---|
| First page | A search, status view or ordinary form with a clear owner |
| Current backend | Language, framework, version and who supports it |
| Ordinary URL | A URL that still returns a complete page |
| Fragment URL | The endpoint returning only the changing section |
| Data boundary | Which signed in roles may read or change each record |
| Failure behavior | Timeout message, validation errors and safe retry |
| Browser acceptance | Keyboard, narrow screen, slow connection, Back and reload |
| Business acceptance | An employee completes the task with realistic test data |
| Operations | Deployment owner, logs, backups, restore test and rollback |
| Spending limit | Setup hours, monthly hours, hosting and usage limits |
Do not migrate an entire website to obtain one useful search box. Keep your first change small enough that it can be removed without moving the database.
## Pick the version deliberately
The [4.0 release announcement](https://four.htmx.org/announcements/2026-08-28-htmx-4.0.0-is-released) presents 4.0 as released while promising continued 2.x support. It does not mandate 4.x for every new project. Our recommendation is to evaluate 4.0 first for a new application, while keeping an existing 2.x application on the latest 2.x until its migration tests pass. Read the project's exact support scope alongside the [upgrade guide](https://jwatte.com/downloads/htmx-upgrade-guide.md). On our reading date the npm `latest` tag was 2.0.11 and `next` was 4.0.0. The [2.x homepage](https://htmx.org/) explains that `latest` stays on 2.x to avoid accidental upgrades, with a change planned sometime in 2027.
Pin an exact version. Do not use a floating CDN URL or assume `npm install htmx.org` installs 4.x. This kit gives explicit install commands for each. It uses attributes directly on the requesting element so its nine patterns work in both versions. In 4.x, inheriting a header or target from a parent is explicit: for example `hx-headers:inherited`. In 2.x the corresponding inherited attribute is `hx-headers`.
## Install method 1: CDN
These are the official published script snippets, with the hashes checked against the files we tested. Use one, never both. The 2.x snippet comes from [its installation documentation](https://htmx.org/docs/#installing); the 4.x snippet comes from [its installation documentation](https://four.htmx.org/docs/).
```html
```
```html
```
Subresource Integrity checks that the response matches the expected bytes. It does not keep the CDN online. For this method, add `https://cdn.jsdelivr.net` to the `script-src` policy below; the self hosted policy otherwise blocks it.
## Install method 2: self host the file
This is our default recommendation for business sites. Your application serves its own copy, with no CDN dependency at runtime. The official [2.x installation page](https://htmx.org/docs/#installing) describes downloading and serving the file; [4.x installation](https://four.htmx.org/docs/) supports the same approach. Put the downloaded file in the public assets directory used by your framework. The generic `public/assets` directory here is an example, not a requirement.
For 2.x:
```sh
mkdir -p public/assets
curl --fail --location https://cdn.jsdelivr.net/npm/htmx.org@2.0.11/dist/htmx.min.js -o public/assets/htmx-2.0.11.min.js
echo 'd6fdc75f204e6bdefa99b69bf1e6d4ac69b8a364f77929f45c13476b4000f717 public/assets/htmx-2.0.11.min.js' | sha256sum --check
```
```html
```
For 4.x:
```sh
mkdir -p public/assets
curl --fail --location https://cdn.jsdelivr.net/npm/htmx.org@4.0.0/dist/htmx.min.js -o public/assets/htmx-4.0.0.min.js
echo 'e484d9171a9db30a39c8f16e3d709d4137f3211c659f8e6125816635033d593f public/assets/htmx-4.0.0.min.js' | sha256sum --check
```
```html
```
Stop if the checksum fails. These SHA256 values are our measurements, not a separate signature from the project. Our starting files came from the npm registry tarballs, each verified against its published SHA512 `dist.integrity`. Preserve the package license notice. Use versioned filenames so a later update cannot be confused with an older cached asset.
## Install method 3: npm and your existing bundler
The official package is `htmx.org`, not `htmx`. Use your existing build system; htmx itself does not require a bundler.
```sh
npm install --save-exact htmx.org@2.0.11
```
Or, for a new 4.x project:
```sh
npm install --save-exact htmx.org@4.0.0
```
The official 4.x docs show the side effect import:
```js
import 'htmx.org';
```
The 2.x docs also describe importing the module where a bundler is already present. Commit the lockfile, build your normal application bundle and serve that bundle from your own origin. A browser only loads the CDN or self hosted script in the first two methods; it does not run npm on the visitor's machine.
## CSP without inline JavaScript or eval
Send this HTTP response header for the self hosted examples:
```http
Content-Security-Policy: default-src 'self'; script-src 'self'; connect-src 'self'; style-src 'self'; img-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self'
```
Put the correct configuration before the htmx script in the page head. For 2.0.11:
```html
```
For 4.0.0:
```html
```
Do not copy the removed 2.x security settings into 4.x and assume they still work. Version 4 uses `mode` for request scope. Its normal history handling fetches a complete page instead of retaining DOM snapshots. The examples use no `hx-on`, JavaScript expressions in attributes, or trigger filters requiring evaluation. The [4.x security documentation](https://four.htmx.org/docs/#security-considerations) explains that avoiding those optional features lets a site omit `unsafe-eval`. For a nonce based application that needs those features, evaluate the official `hx-csp` extension and its `safeEval` option separately.
The minimal policy above does not authorize remote images, analytics, payment frames, inline styles or a CDN. Add only the origins and features your real application needs. Use an external stylesheet for loading indicators. Check browser console errors before enforcing an adapted policy in production.
## CSRF belongs to your server
For ordinary form posts, a hidden framework token works in both htmx versions. Render a fresh token for the current session. A token written permanently into source code provides no protection. HTTPS, secure session cookies and permission checks are still required.
### Django
Keep `CsrfViewMiddleware` enabled and use the documented template tag inside the form:
```html
```
Django's [CSRF guide](https://docs.djangoproject.com/en/5.2/howto/csrf/) also supports `X-CSRFToken`. Its cookie reading advice depends on `CSRF_USE_SESSIONS` and `CSRF_COOKIE_HTTPONLY`; do not assume JavaScript can always read the cookie. Use the DOM token when those settings require it.
### Laravel
Keep the route in the middleware group that verifies CSRF tokens. Blade supplies the hidden input:
```html
```
The [Laravel CSRF guide](https://laravel.com/framework/docs/13.x/csrf) also documents the `X-CSRF-TOKEN` header. If rendering a JSON `hx-headers` attribute, encode the JSON and HTML attribute safely with your template tools; do not concatenate arbitrary user text into it.
### Rails
Use the framework form helper where practical so it generates its authenticity token. The equivalent explicit hidden input is:
```html
```
The [Rails security guide](https://guides.rubyonrails.org/security.html) documents `csrf_meta_tags` and the `X-CSRF-Token` header for JavaScript requests. The [forgery protection API](https://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html) documents `form_authenticity_token`. Assign one library ownership of each form; if another library intercepts it, test that a single click makes only one submission.
### Express
Express does not automatically implement CSRF protection. Its official [session middleware documentation](https://expressjs.com/en/resources/middleware/session.html) describes server sessions and warns that the default `MemoryStore` is unsuitable for production. Choose a maintained session store and implement a reviewed CSRF mechanism, following [OWASP's synchronizer token pattern](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html). This small example shows the check, assuming secure session middleware is already installed:
```js
import { randomBytes, timingSafeEqual } from 'node:crypto';
app.use(express.urlencoded({ extended: false, limit: '20kb' }));
app.get('/contact', (req, res) => {
req.session.csrf ||= randomBytes(32).toString('hex');
res.render('contact', { csrf: req.session.csrf });
});
function requireCsrf(req, res, next) {
const token = req.get('X-CSRF-Token') || req.body.csrf;
const expected = req.session.csrf;
if (typeof token !== 'string' || typeof expected !== 'string') {
return res.sendStatus(403);
}
const a = Buffer.from(token);
const b = Buffer.from(expected);
if (a.length !== b.length || !timingSafeEqual(a, b)) {
return res.sendStatus(403);
}
next();
}
app.post('/contact', requireLogin, requireCsrf, saveContact);
```
Render the `csrf` value in an escaped hidden input named `csrf`. Regenerate session state as your login policy requires, and prevent caching of token bearing private pages. `express.json()` alone does not parse the default URL encoded htmx form body. The code above is a focused check, not a complete authentication system.
For a standalone delete button, send the framework's expected header on that button. The nine browser fixtures below use `test-token` solely to demonstrate transport and rejection of missing tokens. Replace it with a session token and the right header name. Never deploy the literal test token.
## Nine patterns with HTML responses
All request attributes are on the initiating element. The fragments shown have `Content-Type: text/html; charset=utf-8`. Any value originating from a person or database must pass through a template with automatic output escaping. The constant strings below make the response shape easy to see.
### 1. Delayed live search
```html
```
The delay waits until typing pauses. Announce a short result count in the separate status region from an external event listener, rather than making a screen reader recite a whole table after every keystroke. The fixture asserts the row swap; screen reader announcements need a separate accessibility review.
### 2. A form with validation
```html
```
For an empty name, the server returns this section with HTTP 200 in our explicit application result convention:
```html
```
On success it returns:
```html
Saved Avery
```
Many frameworks correctly use HTTP 422 for invalid input. In 2.x, configure response handling or an appropriate extension before expecting 422 to swap. Version 4 swaps error responses by default, except its configured `noSwap` statuses, initially 204 and 304. Do not return raw exception pages into a form. Keep validation consistent, preserve entered values, and move focus to an error summary when needed. Add ordinary `action` and `method` attributes and a full page response when supporting a no JavaScript fallback.
### 3. Load more
```html
```
The server returns the additional record followed by the next control:
```html
Order 21: ready for collection
```
On the last page, omit the next control. Use stable ordering or a cursor if new records can arrive while somebody pages through the list. The previous records stay in the document because only the loading control is replaced.
### 4. Delete a row after confirmation
```html
Draft quotes
Draft 7
```
After checking ownership and recording the deletion, respond with HTTP 200 and an empty HTML body. An empty `outerHTML` swap removes the row. HTTP 204 intentionally performs no swap. A browser confirmation prompt is a usability step; it does not authorize the database change. Test both Cancel and Confirm, as our fixture does.
### 5. Inline edit
```html
Old address
```
The edit endpoint returns:
```html
```
The save endpoint returns the original section shape with the escaped new address. Handle stale edits explicitly: compare a record version or updated timestamp before overwriting somebody else's change.
### 6. Dependent dropdowns
```html
```
`GET /units?site=south` returns options:
```html
```
Check that the selected unit belongs to the selected location again during submission. A filtered dropdown is not an access control.
### 7. Poll a job until it finishes
```html
Working
```
While processing, return the same shape with “Still working.” Once finished, remove the request attributes:
```html
Ready to download
```
The 100 millisecond delay keeps the test short. For an actual export job, choose a much longer interval such as five seconds, based on load and user need. This is load polling: each replacement schedules the next request. The completed fragment stops polling because it has no trigger. Keep the export itself in a server job queue; do not recreate it with every status request.
### 8. A toast driven by a response header
```html
```
The approval response has the header below and the body `
Approved
`:
```http
HX-Trigger: saved
```
The `saved` event triggers `GET /notice`, which returns:
```html
Changes saved.
```
This approach works under the strict CSP without inline event handlers. Ensure the message corresponds to the current user and current operation. A success message must follow a completed transaction, not merely acceptance into a queue.
### 9. Ordinary pagination with an enhanced link
```html
Previous page
```
The real endpoint must also return a complete page for ordinary navigation. The fixture checks the enhanced swap. For bookmarkable filters and pages, decide whether to add `hx-push-url="true"`, then test direct entry, refresh and Back before shipping. Do not claim a history behavior has been tested just because the table changed.
## Return the right response
Both versions send `HX-Request: true`. It tells the server this is an htmx request, not who is authorized. Version 4 also has `HX-Request-Type`, distinguishing full from partial requests. Use an explicit fragment route when possible; it makes caching and testing easier.
When one URL returns a page or a fragment based on `HX-Request`, add `Vary: HX-Request`. If the representation also varies on the 4.x request type, vary on that header too. Private account data should normally avoid shared caching altogether. A cached fragment must never become the complete page opened from a bookmark.
Use `HX-Redirect` to request a full navigation, such as returning to sign in after a session expires. Send it on a response whose headers htmx can process; a browser follows a normal HTTP redirect before htmx sees the original response headers. `HX-Trigger` dispatches application events, as pattern 8 shows.
## Security and accessibility checks
- Escape all untrusted template values; review every raw HTML insertion.
- Reject missing or invalid CSRF tokens and unauthorized record IDs on the server.
- Make GET endpoints read only. Do not approve orders or delete records in a GET handler.
- Restrict requests to your origin, preserve CSP, and review third party scripts separately.
- Put untrusted rich content in `hx-disable` containers on 2.x or `hx-ignore` on 4.x, in addition to proper sanitization. These do not replace output escaping.
- Keep private information out of history snapshots. Review back navigation and shared computer behavior.
- Label controls, keep stable input IDs and preserve useful focus through a swap.
- Use a short polite live region for status. Avoid announcing an entire changing table repeatedly.
- Give errors text, field associations and a sensible focus target. Do not communicate failure with color alone.
- Test touch and keyboard operation, zoom, screen reader output and slow or failed requests.
## Testing and troubleshooting
Our browser run covered each pattern on both versions and found no CSP console errors. That proves the narrow fixture interactions. It does not certify your backend's permissions, real screen reader experience, provider billing or production performance.
| Symptom | Check first | Fix or next test |
|---|---|---|
| Nothing happens | Script response, CSP console, version, event and URL | Check the actual network request and asset checksum |
| A whole page appears inside a section | Endpoint returned its layout | Render the same partial without the outer layout for this request |
| CSRF 403 | Cookie, session, field or header name, token freshness | Keep middleware enabled and pass the framework token correctly |
| Invalid form does not update in 2.x | Response status is 422 | Add deliberate response handling or the documented extension |
| Exception page appears in 4.x | Error responses swap by default | Return safe fragments and configure error routing or `noSwap` |
| Back displays only a fragment | Mixed cache entries or wrong history response | Correct `Vary`, return full pages for navigation and retest |
| CSP blocks execution | Inline script, eval feature or CDN not allowed | Remove the feature or explicitly design a permitted alternative |
| Headers disappear after moving to 4.x | Header was on a parent | Use `hx-headers:inherited` or put the header on the requester |
| Delete does not remove the row | Response is 204 | Use 200 with empty body for the tested `outerHTML` pattern |
| Polling never ends | Completed response still has a load trigger | Return a finished fragment without request attributes |
| User loses their place | Focused node removed or ID changed | Preserve IDs and deliberately focus the next useful control |
On 2.x, listen for documented events such as `htmx:responseError` and `htmx:sendError` in an external script. Version 4 changes event names and details; use `htmx:response:error` and the general `htmx:error` as documented in [its events reference](https://four.htmx.org/reference/events). Test a timeout, network loss, denied access, missing token, expired login, validation failure and a double click. Check that the visible message matches whether the server actually saved the change.
## Cost worksheet
Prices were read November 1, 2026 and are US dollars before tax, billed monthly. Hours and workload quantities are our planning assumptions. Fill both a low and a high case. Use the same labor rate for competing approaches doing comparable work.
| Input | Your low case | Your high case |
|---|---|---|
| Developer hourly compensation or quoted rate | ____ | ____ |
| Development setup hours | ____ | ____ |
| Development upkeep hours per month | ____ | ____ |
| Administrator hourly compensation or quoted rate | ____ | ____ |
| Server setup hours | ____ | ____ |
| Server administration hours per month | ____ | ____ |
| Server price per month and number of servers | ____ | ____ |
| Retained backup GB including versions | ____ | ____ |
| Backup price per month, including transfer and requests | ____ | ____ |
| Platform seats and recurring price | ____ | ____ |
| Platform usage charges per month | ____ | ____ |
| Data migration, training and specialist assessment quote | ____ | ____ |
Setup = development hours x developer rate + server setup hours x administrator rate. Monthly = hosting + backups + administration hours x administrator rate + development upkeep hours x developer rate + platform seats + usage charges. Total over 36 months = setup + 36 x monthly. Difference = alternative total minus the proposed total.
When the proposed setup costs more but its monthly cost is lower, break even = round up the setup difference divided by monthly savings. If it starts cheaper and remains cheaper, the break even month is zero. If it costs more both initially and monthly, it never breaks even from cost savings alone. Price benefits such as fewer mistakes separately and do not count unspent employee time as cash saved.
### Verified inputs used in the filled examples
The BLS May 2025 national median hourly wage is $44.54 for [Web Developers, SOC 15-1254](https://data.bls.gov/oesprofile/?areas=INDUSTRY,STATE,MSA&major_group=150000&measure=01&occupation=151254&year=2025) and $65.38 for [Software Developers, SOC 15-1252](https://data.bls.gov/oesprofile/?areas=INDUSTRY,STATE,MSA&major_group=150000&measure=01&occupation=151252&year=2025). Divide by the June 2026 private industry [wages share of compensation, 0.700](https://data.bls.gov/timeseries/CMU2020000000000P): $44.54 / 0.700 = $63.63 per hour, rounded; $65.38 / 0.700 = $93.40. These are employee compensation estimates, not agency billing rates.
Administration uses $68.09 per hour from the [May 2025 systems administrator median](https://data.bls.gov/oesprofile/?areas=INDUSTRY,STATE,MSA&major_group=150000&measure=01&occupation=151244&year=2025), $47.66 / 0.700, read October 29, 2026. An external provider will add its own overhead and margin; get a quote.
[DigitalOcean Basic Regular](https://www.digitalocean.com/pricing/droplets) costs $24 per month for 2 vCPU, 4 GiB RAM and 80 GiB disk, or $48 for 4 vCPU, 8 GiB RAM and 160 GiB disk. These are the full-month caps. [Backblaze B2](https://www.backblaze.com/cloud-storage/pricing) starts at $6.95 per TB per month, with the first 10 GB free. Our assumed 100 GB retained costs (100 - 10) x $0.00695 = $0.6255, budgeted at $0.63 per month. For 500 GB, the result is $3.4055, budgeted at $3.41. The tables deliberately use these two-decimal backup allowances. They assume no chargeable transfer or API usage.
[Vercel Pro](https://vercel.com/docs/plans/pro-plan) costs $20 per month, including one deploying seat and $20 monthly usage credit. Additional deploying seats cost $20 each per month; viewer seats are free. Additional usage and paid add-ons increase the bill. Read the [pricing page](https://vercel.com/pricing) before purchase.
### A: add features to an existing internal tool
Both approaches use the $63.63 web developer rate. Our htmx assumptions are 40 to 80 setup hours and 1 to 2 development upkeep hours per month. React with a separate API assumes 120 to 240 setup hours, including API work, and 3 to 6 upkeep hours per month. Both retain the $24 server, $0.63 backup allowance and 1 to 3 monthly administration hours at $68.09. Existing server setup cancels.
| Approach | Setup, low / high | Monthly, low / high | 36 months, low / high |
|---|---:|---:|---:|
| Add htmx | $2,545 / $5,090 | $156 / $356 | $8,174 / $17,912 |
| React plus separate API | $7,636 / $15,271 | $284 / $611 | $17,846 / $37,256 |
| Difference | | | $9,672 / $19,344 |
Htmx low case = 40 x $63.63 + 36 x ($24 + $0.63 + $68.09 + $63.63) = $8,173.80, rounded $8,174. Break even is month zero under these assumptions. The hours are estimates, not measured productivity gains. If your React team already has the required API and components, its quote may be lower.
### B: host everything together or add a frontend platform
This compares frontend hosting while retaining the same backend. Both use the $24 DigitalOcean server, $0.63 backup allowance, 4 to 12 infrastructure setup hours and 1 to 3 monthly administration hours at $68.09. The alternative adds Vercel Pro for the frontend with one deploying seat and no usage overage assumed. Application development and upkeep are identical and excluded from both sides.
| Hosting layout | Setup, low / high | Monthly, low / high | 36 months, low / high |
|---|---:|---:|---:|
| Combined server | $272 / $817 | $93 / $229 | $3,610 / $9,057 |
| Vercel Pro frontend plus same backend | $272 / $817 | $113 / $249 | $4,330 / $9,777 |
| Difference | | | $720 / $720 |
Combined low case = 4 x $68.09 + 36 x ($24 + $0.63 + $68.09) = $3,610.28. Vercel adds 36 x $20 = $720. Break even is month zero with matched labor. Its preview and collaboration tools may justify the fee: saving $20 / $68.09 = 0.294 administration hours monthly offsets it. This is not a quote for a completely managed application or database.
### C: one codebase or separate frontend and backend work
Both use $93.40 per developer hour. Our one-codebase assumptions are 400 to 800 setup hours and 12 to 24 monthly upkeep hours. Separate frontend and API work totals 600 to 1,200 setup hours and 20 to 40 monthly upkeep hours. These are assigned project hours, not two full time salaries. Both use a $48 server, $3.41 backup allowance, 4 to 12 infrastructure setup hours and 1 to 3 monthly administration hours at $68.09.
| Approach | Setup, low / high | Monthly, low / high | 36 months, low / high |
|---|---:|---:|---:|
| One server rendered codebase | $37,632 / $75,537 | $1,240 / $2,497 | $82,283 / $165,439 |
| Separate frontend and API | $56,312 / $112,897 | $1,988 / $3,992 | $127,862 / $256,598 |
| Difference | | | $45,579 / $91,158 |
One-codebase low case = 400 x $93.40 + 4 x $68.09 + 36 x ($48 + $3.41 + $68.09 + 12 x $93.40) = $82,283.16. Break even is month zero in this example. A framework and separate API can be the better investment when mobile applications and integrations need the API anyway, or when complex client state justifies it. These estimates exclude major migration, training, compliance work, incidents and on-call coverage.
## Sources and reading dates
Read November 1, 2026 unless a baseline date is specified:
1. [htmx 2.x homepage](https://htmx.org/), version tag explanation and published size claim; 17:54 UTC.
2. [htmx 2.x documentation](https://htmx.org/docs/), installation, configuration, security, events, response handling and caching; 17:54 UTC.
3. [htmx 2.x reference](https://htmx.org/reference/), attributes and headers; 17:54 UTC.
4. [htmx 4.x documentation](https://four.htmx.org/docs/), installation, inheritance, polling, security and responses; 17:55 UTC.
5. [htmx 4.x configuration](https://four.htmx.org/reference/config/), available configuration names; 17:55 UTC.
6. [htmx 4.0 release](https://four.htmx.org/announcements/2026-08-28-htmx-4.0.0-is-released), version choice; 17:55 UTC.
7. [npm registry metadata](https://registry.npmjs.org/htmx.org), tags, dates and tarball integrity; 17:54 UTC.
8. [Django CSRF](https://docs.djangoproject.com/en/5.2/howto/csrf/), hidden token and headers.
9. [Laravel CSRF](https://laravel.com/framework/docs/13.x/csrf), Blade token and headers.
10. [Rails security](https://guides.rubyonrails.org/security.html), authenticity tokens and JavaScript requests.
11. [Express API](https://expressjs.com/en/5x/api.html) and [session middleware](https://expressjs.com/en/resources/middleware/session.html), request parsing and sessions.
12. [OWASP CSRF prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html), synchronizer tokens.
13. [MDN Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy), HTTP policy directives.
14. [DigitalOcean Droplets](https://www.digitalocean.com/pricing/droplets) and [Backblaze B2](https://www.backblaze.com/cloud-storage/pricing), shared baseline read October 29, 2026 and rechecked November 1, 2026.
15. [BLS administrator profile](https://data.bls.gov/oesprofile/?areas=INDUSTRY,STATE,MSA&major_group=150000&measure=01&occupation=151244&year=2025) and [ECEC wages share](https://data.bls.gov/timeseries/CMU2020000000000P), shared baseline read October 29, 2026.
For version changes, use the [htmx Upgrade Guide](https://jwatte.com/downloads/htmx-upgrade-guide.md). For a gradual replacement of existing interactions, use the [Site Upgrade Playbook](https://jwatte.com/downloads/htmx-site-upgrade-playbook.md).