---
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/v0.2"
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/v0.2/installation): Add the package to a new or existing Bun project.
- [Theming](https://susonwaiba.github.io/htmx-ui/docs/v0.2/theming): Override design tokens or build a custom theme.
- [Dark mode](https://susonwaiba.github.io/htmx-ui/docs/v0.2/dark-mode): How dark mode works and how to add a switcher.
- [Components](https://susonwaiba.github.io/htmx-ui/docs/v0.2/components): Browse every component with live examples.

## 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.

## 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.
