FUNSTACK UI

HomeWhyGetting startedConceptsComponentsRoadmap
GitHub

FUNSTACK UI is experimental. Part of the FUNSTACK family with FUNSTACK Router and FUNSTACK Static, which build this site.

Introduction

  • Why FUNSTACK UI
  • Overview
  • Getting started

Concepts

  • Action props
  • Pending & optimistic state
  • Transitions & ViewTransition
  • Suspense
  • Browser-owned state
  • Server Components

Guides

  • Experimental features
  • Content Security Policy
  • Using with AI agents

Components

  • Buttons
    • Button
    • Toggle
    • ToggleGroup
    • Toolbar
  • Disclosure
    • Tabs
    • Accordion
  • Overlays
    • Tooltip
    • Popover
    • Dialog
    • Menu
    • Context menu (Menu)
    • Toast
  • Forms
    • Field and Form
    • Checkbox
    • CheckboxGroup
    • RadioGroup
    • Switch
    • Select
    • Combobox
    • Slider
    • NumberField
  • Output
    • Progress
    • Meter

Resources

  • Roadmap & changelog
  • GitHub ↗

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

    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

    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

    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

    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

    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

    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:

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:

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

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 libraryFUNSTACK UI
CallbacksEvent handlers (onValueChange, onOpenChange). Transitions, optimistic state and pending flags are yours to add.Action props: run in a transition, may be async. Optimistic and pending state built in.
Content that loads dataUsually updated urgently, so content that suspends shows its fallback unless you add a transition.Old content stays on screen until the new content is ready.
PopupsJavaScript for positioning, layering and dismissal, so older browsers work too.The platform: Popover API, <dialog>, CSS Anchor Positioning. Some components work before hydration.
AnimationCSS, with exit-animation states built in. No <ViewTransition> integration.CSS, plus <ViewTransition> through the parts' transition prop (Transitions).
BrowsersWide support, including older versions.Latest stable Chrome, Edge, Safari and Firefox. No fallbacks.
MaturityStable 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.
  • 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).

Next

  • Getting started: install, import, style.
  • Action props: the idea everything else builds on.
  • Using with AI agents: give your coding agent the full docs.