---
title: "Introduction"
description: "HTMX UI is a set of Tailwind CSS components for apps where the server renders HTML and htmx swaps it into the page."
url: "https://susonwaiba.github.io/htmx-ui/docs"
section: "Getting started"
---

# Introduction

HTMX UI is a set of Tailwind CSS components for apps where the server renders HTML and htmx swaps it into the page.

- [Installation](https://susonwaiba.github.io/htmx-ui/docs/installation): Add the package to a new or existing Bun project.
- [Theming](https://susonwaiba.github.io/htmx-ui/docs/theming): Override design tokens or build a custom theme.
- [Dark mode](https://susonwaiba.github.io/htmx-ui/docs/dark-mode): How dark mode works and how to add a switcher.
- [Components](https://susonwaiba.github.io/htmx-ui/docs/components): Browse every component with live examples.
- [Upgrading](https://susonwaiba.github.io/htmx-ui/docs/upgrading): One command rewrites what a release renamed.

## What it is

Most component libraries assume a JavaScript framework owns the page. HTMX UI assumes the opposite: your server returns HTML, htmx swaps it in, and components need to look right and keep working when they arrive inside a fragment.

So every component is:

- **Markup and classes.** A button is `<button class="btn btn-primary">`. Any server language can render it.
- **Styled with Tailwind v4.** Component classes live in `@layer components`, so a utility class on the same element always wins.
- **Themed with CSS variables.** Colours and radius are variables like `--primary` and `--radius`, exposed to Tailwind as `bg-primary`, `text-muted-foreground` and so on.
- **Behaviour only where needed.** Things like copy buttons and the mobile sidebar are tiny TypeScript modules that re-run after every htmx swap.

## A quick look

Components compose like plain HTML. This card uses the card, badge and button components together:

```html
<div class="card w-full max-w-sm">
  <div class="card-header pb-6">
    <div class="flex items-center justify-between">
      <h3 class="card-title">Deploy preview</h3>
      <span class="badge badge-success badge-dot">Ready</span>
    </div>
    <p class="card-description">Built from main in 42 seconds.</p>
  </div>
  <div class="card-footer pt-6">
    <button class="btn btn-primary btn-sm">Open preview</button>
    <button class="btn btn-ghost btn-sm">View logs</button>
  </div>
</div>
```

## Working with htmx

The entry script calls `initComponents(document)` once the page loads, and again with each newly inserted element on the `htmx:after:process` event. Each initialiser only touches elements it hasn't seen yet (it marks them with `data-init`), so a fragment's components come alive without anything running twice.

```ts app.ts
import "htmx.org";
import { initComponents } from "htmx-ui";

document.addEventListener("DOMContentLoaded", () => initComponents(document));
document.addEventListener("htmx:after:process", (e) => initComponents(e.target ?? document));
```

> **Why not htmx:after:swap?** In htmx 4, `htmx:after:swap` is dispatched on the element that made the request, so its target is not the new content. `htmx:after:process` fires on each element htmx inserts, which is exactly what needs initialising.

## Upgrading

Before 1.0 a minor release can rename a class or change a component's markup, and every one of them is listed under _Breaking changes_ in the [changelog](https://susonwaiba.github.io/htmx-ui/docs/changelog). You don't make those edits by hand: `htmx-ui-upgrade` rewrites your templates, scripts, stylesheets and config to the release you are moving to, then prints the places it left for you to check. It refuses to run on uncommitted changes, so an upgrade is one diff to review — or to undo. See [Upgrading](https://susonwaiba.github.io/htmx-ui/docs/upgrading).

## What it's built on

- [htmx 4](https://htmx.org) for requests and swaps.
- [Tailwind CSS v4](https://tailwindcss.com) for styling and tokens.
- [Bun](https://bun.sh) for bundling, the dev server and tests.
- [Nunjucks](https://mozilla.github.io/nunjucks/) macros (optional) for less repetitive markup.
