# Why FUNSTACK UI

React 19 added actions, transitions, `useOptimistic`, Suspense for data and `<ViewTransition>`. FUNSTACK UI is a headless component library that uses them for you, so the async work around every button, tab and dialog is already done.

## What you stop writing

- **[No pending flags](concepts/pending-and-optimistic.md)**

  Every component with an action sets `data-pending` while the action runs, until its result is on screen. Style it in CSS.

- **[No onChange + useTransition + useOptimistic](concepts/action-props.md)**

  Pass an async function as an action prop. The component runs it in a transition, shows the new value at once, and rolls it back if the action fails. Errors go to your Error Boundary.

- **[No races between fast clicks](concepts/action-props.md#overlapping-actions)**

  The display always shows the latest click. Uncontrolled state commits only the latest one, so a slow response to an older click never overwrites a newer choice.

- **[No fallback flashes](concepts/suspense.md)**

  When the next tab, accordion item or dialog loads data, the current screen stays as it is and the trigger shows pending until the new content is ready.

- **[No JavaScript copies of the browser](concepts/browser-owned-state.md)**

  Popups use the Popover API, `<dialog>` and CSS Anchor Positioning; form controls are native inputs. The browser handles the top layer and Escape, and popovers close on a click outside natively.

- **[No animation glue](concepts/transitions.md)**

  Pass `transition` to a part and it renders React's `<ViewTransition>`. Each change is tagged with a transition type, such as `fsui-tabs-next`, so you can animate by direction.

## Before and after

A favourite button that saves to a server. By hand:

```tsx
const [pressed, setPressed] = useState(initial);
const [optimistic, setOptimistic] = useOptimistic(pressed);
const [isPending, startTransition] = useTransition();

<button
  aria-pressed={optimistic}
  data-pending={isPending ? "" : undefined}
  onClick={() => {
    const next = !optimistic;
    startTransition(async () => {
      setOptimistic(next);
      await saveFavorite(next);
      startTransition(() => setPressed(next));
    });
  }}
>
  Favorite
</button>
// ...and a slow save from an earlier click can still overwrite a later one.
```

With FUNSTACK UI:

```tsx
<Toggle defaultPressed={initial} pressedChangeAction={(next) => saveFavorite(next)}>
  Favorite
</Toggle>
```

Same optimistic flip and `data-pending`, plus latest-click-wins and transition types for animation. Try it on the [home page](index.md).

## Compared with other headless libraries

Most established headless React libraries were designed before React's async features, and support a wide range of browsers. They are mature and a safe choice. FUNSTACK UI takes a different approach:

| | Typical headless library | FUNSTACK UI |
| - | - | - |
| **Callbacks** | Event handlers (`onValueChange`, `onOpenChange`). Transitions, optimistic state and pending flags are yours to add. | [Action props](concepts/action-props.md): run in a transition, may be async. Optimistic and pending state built in. |
| **Content that loads data** | Usually updated urgently, so content that suspends shows its fallback unless you add a transition. | [Old content stays on screen](concepts/suspense.md) until the new content is ready. |
| **Popups** | JavaScript for positioning, layering and dismissal, so older browsers work too. | The [platform](concepts/browser-owned-state.md): Popover API, `<dialog>`, CSS Anchor Positioning. Some components work before hydration. |
| **Animation** | CSS, with exit-animation states built in. No `<ViewTransition>` integration. | CSS, plus `<ViewTransition>` through the parts' `transition` prop ([Transitions](concepts/transitions.md)). |
| **Browsers** | Wide support, including older versions. | Latest stable Chrome, Edge, Safari and Firefox. No fallbacks. |
| **Maturity** | Stable APIs, large component sets. | Experimental alpha. APIs change without notice. |

## Who it is for

### Choose it if

- Your app uses React 19.3 or later, with actions, Suspense or [Server Components](concepts/server-components.md).
- Your controls save to a server and should feel instant.
- You only need to support current browsers.
- You want to write your own CSS: no styles, no theme, no provider.

### Look elsewhere (for now) if

- You need older browsers or older Safari versions.
- You need a stable API today.
- You want styled components out of the box.
- You need a component that is not here yet (see the [roadmap](roadmap.md)).

## Next

- [Getting started](getting-started.md): install, import, style.
- [Action props](concepts/action-props.md): the idea everything else builds on.
- [Using with AI agents](concepts/ai-agents.md): give your coding agent the full docs.
