Skip to content

Actions

Status

A polite live message for advisory feedback that should not interrupt the current task.

When to use it: Use it for saves, background updates, and other useful but non-urgent results.

On this page

Example

Loading example…
Status.tsxtsx
import { useState } from "react";
import { Button, Status } from "@comp0/react";

export function Example() {
  const [saved, setSaved] = useState(false);
  return (
    <div className="flex w-full max-w-80 flex-col items-start gap-3 text-base sm:text-sm">
      <Button
        className="min-h-10 rounded-lg bg-teal-700 px-3 py-2 font-medium text-white outline-offset-2 focus-visible:outline-2 focus-visible:outline-teal-600 dark:bg-teal-400 dark:text-zinc-950 dark:focus-visible:outline-teal-300"
        onClick={() => setSaved(true)}
      >
        Save draft
      </Button>
      {saved && (
        <Status className="w-full rounded-lg border border-teal-700/25 bg-teal-50 p-3 text-teal-950 dark:border-teal-400/25 dark:bg-teal-950 dark:text-teal-100">
          Draft saved.
        </Status>
      )}
    </div>
  );
}

Inline approval request

Compose advisory status, expandable evidence, and one-shot actions into a human approval checkpoint that resolves without opening a modal or stealing focus.

Loading example…
status.approval-request.tsxtsx
import { useEffect, useState } from "react";
import { Button, Disclosure, DisclosurePanel, DisclosureTrigger, Status } from "@comp0/react";

type ApprovalState = "pending" | "submitting" | "approved" | "rejected";

const stateLabels: Record<ApprovalState, string> = {
  pending: "Approval needed",
  submitting: "Applying decision",
  approved: "Approved",
  rejected: "Rejected",
};

export function Example() {
  const [approvalState, setApprovalState] = useState<ApprovalState>("pending");

  useEffect(() => {
    if (approvalState !== "submitting") return;
    const timer = setTimeout(() => setApprovalState("approved"), 800);
    return () => clearTimeout(timer);
  }, [approvalState]);

  const pending = approvalState === "pending";
  const submitting = approvalState === "submitting";
  const resolved = approvalState === "approved" || approvalState === "rejected";

  return (
    <section
      aria-labelledby="approval-title"
      aria-busy={submitting || undefined}
      className="w-full max-w-md rounded-xl border border-amber-600/25 bg-amber-50 p-4 text-zinc-950 dark:border-amber-400/25 dark:bg-amber-950/40 dark:text-white"
    >
      <div className="flex items-start justify-between gap-4">
        <div>
          <h2 id="approval-title" className="font-semibold">
            Publish three documentation pages?
          </h2>
          <p className="mt-1 text-sm text-zinc-600 dark:text-zinc-300">
            This action updates the public production site.
          </p>
        </div>
        <span
          aria-hidden="true"
          data-state={approvalState}
          className="shrink-0 rounded-full bg-amber-100 px-2 py-1 text-xs font-medium text-amber-900 data-[state=approved]:bg-emerald-100 data-[state=approved]:text-emerald-900 data-[state=rejected]:bg-rose-100 data-[state=rejected]:text-rose-900 dark:bg-amber-900 dark:text-amber-100 dark:data-[state=approved]:bg-emerald-900 dark:data-[state=approved]:text-emerald-100 dark:data-[state=rejected]:bg-rose-900 dark:data-[state=rejected]:text-rose-100"
        >
          {stateLabels[approvalState]}
        </span>
      </div>
      <Disclosure className="mt-3 border-t border-amber-900/10 pt-3 dark:border-amber-100/10">
        <DisclosureTrigger className="cursor-pointer text-sm font-medium outline-teal-600 focus-visible:outline-2 dark:outline-teal-400">
          Review requested changes
        </DisclosureTrigger>
        <DisclosurePanel className="pt-2 text-sm text-zinc-600 dark:text-zinc-300">
          <ul className="list-disc space-y-1 pl-5">
            <li>Publish the Charts, Messages, and Tree pages.</li>
            <li>Invalidate the documentation cache.</li>
            <li>Keep the previous deployment available for rollback.</li>
          </ul>
        </DisclosurePanel>
      </Disclosure>
      <div className="mt-4 flex flex-wrap gap-2">
        {!resolved && (
          <>
            <Button
              disabled={!pending}
              className="rounded-lg bg-emerald-700 px-3 py-2 text-sm font-medium text-white outline-emerald-700 focus-visible:outline-2 disabled:opacity-50 dark:bg-emerald-400 dark:text-zinc-950 dark:outline-emerald-300"
              onClick={() => setApprovalState("submitting")}
            >
              {submitting ? "Approving…" : "Approve publish"}
            </Button>
            <Button
              disabled={!pending}
              className="rounded-lg border border-zinc-950/15 px-3 py-2 text-sm font-medium outline-rose-600 focus-visible:outline-2 disabled:opacity-50 dark:border-white/15 dark:outline-rose-400"
              onClick={() => setApprovalState("rejected")}
            >
              Reject
            </Button>
          </>
        )}
        {resolved && (
          <Button
            className="rounded-lg border border-zinc-950/15 px-3 py-2 text-sm font-medium outline-teal-600 focus-visible:outline-2 dark:border-white/15 dark:outline-teal-400"
            onClick={() => setApprovalState("pending")}
          >
            Reset example
          </Button>
        )}
      </div>
      <Status className="sr-only" aria-atomic="true">
        {stateLabels[approvalState]}
      </Status>
    </section>
  );
}

Anatomy

Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.

A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.

  1. Status

    Polite live message rendered with role=status. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Mount Status when new advisory information is ready.

  2. 2

    Add the supporting parts

    Keep its message short and visible as well as announced.

  3. 3

    Make the behavior clear

    Use Alert instead only when the message genuinely demands immediate attention.

    Exampletsx
    {
      saved && <Status>Draft saved.</Status>;
    }

Keyboard

Forms and accessibility

No form value; use it to report an advisory form or application result.

Accessibility checklist

  • Mount or update Status for advisory information that should wait until the current announcement finishes.
  • Keep the same result visible instead of relying on the live announcement alone.
  • Use Alert only when delaying the message could cause a real problem.

API reference

Importtsx
import { Button, Status } from "@comp0/react";

Status

DOM element

Polite live message rendered with role=status.

PropTypeDescription
childrenReactNodeVisible advisory message.

Keep exploring