Actions

Action Toggle

An asynchronous toggle that commits its pressed state only after the action succeeds.

⠋

Install

npx shadcn@latest add https://4af.selfsimilar.dev/r/action-toggle.json

Usage

import { ActionToggle } from "@/components/ui/action-toggle";

<ActionToggle action={(following) => setFollowing(following)}>
  Follow
</ActionToggle>

API

Main export: ActionToggle

A toggle that runs an async action and changes state only when it succeeds. Use it for states such as Follow/Following or Subscribe/Subscribed.

loader={<MySpinner />} supplies the pending indicator. Otherwise it inherits LoaderProvider, defaulting to AsciiLoader. A supplied icons.pending takes precedence.

Persistent state

Action Button shows a temporary outcome, then returns to idle. Use Action Toggle when the new state should persist:

<ActionToggle
  action={(following) => (following ? follow(id) : unfollow(id))}
  icon={<Plus />}
  icons={{ pressed: <Check /> }}
  labels={{ pressed: "Following" }}
>
  Follow
</ActionToggle>

It sets aria-pressed, which is why it is a separate component, as Toggle is from Button.

It is not optimistic. The label stays and the loader runs until the action settles. On failure it shows the error briefly and keeps its previous state. onPressedChange fires only on success.

With fillIcon and no icons.pressed, the same glyph fills when pressed instead of being replaced.