AI Interface Components

Agent Task Queue

An agent task queue React component with drag-to-reorder, a breathing pulse on the running item, and finished tasks sliding out into a collapsible Done group.

Put it in a chat sidebar or below a message to show what the agent is about to do next. Unlike a fixed-order plan checklist, this is a queue that keeps getting consumed: users can drag to reorder or remove queued tasks, and failed tasks stay at the top of the queue waiting for a retry. Tasks are split into three sections — Running, Queued and Done. The running row breathes to show the agent is at work; when a task finishes, its loader cross-fades into a check mark, holds for a beat, then the whole row slides out and lands at the top of the Done group while the remaining rows FLIP into place. tasks is controlled by the parent; the component only presents the state changes.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add agent-task-queue.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

450
1.6
Active edited usageDefault props
<AgentTaskQueue/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add https://webberui.com/r/agent-task-queue.en.json

Or, once registries are configured in components.json, install it as @webberui/agent-task-queue.

Usage

import { AgentTaskQueue, type AgentTask } from "@/components/ui/agent-task-queue";

const [tasks, setTasks] = React.useState<AgentTask[]>([
  { id: "read", title: "Read this week's calendar", status: "running" },
  { id: "sum", title: "Write the summary", meta: "~2 min", status: "queued" },
  { id: "send", title: "Email the draft to myself", status: "queued" },
]);

<AgentTaskQueue
  tasks={tasks}
  onReorder={(ids) =>
    setTasks((prev) => [
      ...prev.filter((t) => t.status !== "queued"),
      ...ids.map((id) => prev.find((t) => t.id === id)!),
    ])
  }
  onRemove={(id) => setTasks((prev) => prev.filter((t) => t.id !== id))}
  onRetry={(id) =>
    setTasks((prev) => prev.map((t) => (t.id === id ? { ...t, status: "queued" } : t)))
  }
/>

Props

PropTypeDefaultDescription
tasksAgentTask[]—The task list (controlled data); the parent updates statuses and the component animates rows in and out accordingly
onReorder(ids: string[]) => void—Called after queued tasks are reordered, with the new id order of the queued tasks; the drag handles are hidden when omitted
onRemove(id: string) => void—Called when a queued task is removed; when provided, an X button appears on hover / focus
onRetry(id: string) => void—Called when a failed task is retried; when provided, failed rows show a Retry button
doneOpenboolean—Controlled open state of the Done group; managed internally when omitted
defaultDoneOpenbooleanfalseInitial open state of the Done group in uncontrolled mode
onDoneOpenChange(open: boolean) => void—Called when the Done group expands or collapses
doneLabelstring"Done"Heading text of the Done group (the Chinese build defaults to "已完成", "Done")
maxDoneVisiblenumber20Maximum rows shown in the Done group; the rest are summarised as "N more"
stiffnessnumber450Stiffness of the layout spring (shared by reordering and slide in / out)
dampingnumber36Damping of the layout spring
pulseDurationnumber1.6Seconds per breath of the running item
colorDurationnumber150Colour transition time in ms, written to --wb-agent-task-queue-duration
classNamestring—Passed through to the outermost container

AgentTask

FieldTypeDefaultDescription
idstring—Unique id used for the React key, drag reordering and layout animation pairing
titlestring—Task title
detailstring—Secondary description
status"queued" | "running" | "done" | "failed" | "cancelled"—Current status
iconReactNode—Decorative icon left of the title
metastring—Extra info on the right, e.g. "~2 min"

How it works

  • Three sections: running goes to Running, pinned to the top and not draggable; queued and failed go to Queued, with failed pinned to the top in red; done and cancelled go to Done, with cancelled struck through.
  • Finishing slide-out: when a status becomes done, the loader cross-fades into a check mark over 150ms, holds for 300ms, then the row slides into the top of the Done group with a layout animation. If the group is collapsed, the row fades out instead, the header count rolls up digit by digit and the header flashes green.
  • Only the grip handle on the left picks a row up. While dragging, the order is held inside the component and onReorder is called only on release; if the agent starts one of the queued tasks mid-drag, it is dropped from the draft order automatically. If the parent does not update tasks, the order snaps back (controlled data).
  • No drag library is used — reordering and the FLIP fill-in come from Reorder and the layout spring (stiffness / damping) in motion/react.
  • Colour transitions read the CSS variable --wb-agent-task-queue-duration, 150ms by default, adjustable with colorDuration.

Accessibility

  • Each section is a role="list", each row a listitem with hidden status text (queued / running / done / failed / cancelled)
  • Status changes are announced through aria-live="polite", e.g. "Done: Write the summary"
  • Keyboard reordering: focus a grip handle and press Space to enter move mode (aria-pressed="true"), use the Up / Down arrows to swap, Space to drop and call onReorder, Escape to restore; moving focus away during move mode counts as a drop
  • The Done header is a button with aria-expanded and aria-controls; Remove and Retry buttons carry an aria-label with the task name, and the Remove button stays visible on touch devices
  • When the user has enabled reduce motion: no breathing, no spinning and no sliding — rows switch places directly with a 120ms fade; dragging still works but rows do not scale up