For AI agents: the complete documentation index is available at /ooui-react/en/llms.txt, the full documentation bundle is available at /ooui-react/en/llms-full.txt, and this page is available as Markdown at /ooui-react/en/components/progress-bar/index.md.
  • English
  • ProgressBar

    Source | Original component

    A progress bar.

    Determinate progress

    progress takes a 0–100 number; out-of-range values are clamped to [0, 100] (the original doesn't clamp — a NaN produces an invalid width and attribute value).

    import { useState } from "react";
    import { Button, ProgressBar } from "ooui-react";
    
    function App() {
      const [progress, setProgress] = useState(40);
    
      return (
        <div style={{ display: "flex", flexDirection: "column", gap: 12, maxWidth: 320 }}>
          <ProgressBar progress={progress} />
          <div style={{ display: "flex", gap: 8 }}>
            <Button onClick={() => setProgress((p) => Math.max(0, p - 10))}>-10</Button>
            <Button onClick={() => setProgress((p) => Math.min(100, p + 10))}>+10</Button>
          </div>
        </div>
      );
    }
    
    export default App;

    Indeterminate progress

    progress={false} (also the default) means "in progress" with an unknown total: the fill indicates activity with a sliding animation and reports no specific value.

    import { ProgressBar } from "ooui-react";
    
    function App() {
      return (
        <div style={{ maxWidth: 320 }}>
          <ProgressBar />
        </div>
      );
    }
    
    export default App;

    Advancing with an async task

    A common pattern is to start at indeterminate progress and switch to a determinate value once quantifiable progress is available.

    import { useEffect, useState } from "react";
    import { Button, ProgressBar } from "ooui-react";
    
    function App() {
      const [state, setState] = useState<"idle" | "starting" | "running" | "done">("idle");
      const [progress, setProgress] = useState(0);
    
      useEffect(() => {
        if (state !== "running") {
          return;
        }
        const timer = setInterval(() => {
          setProgress((p) => {
            if (p >= 100) {
              setState("done");
              return 100;
            }
            return p + 10;
          });
        }, 300);
        return () => clearInterval(timer);
      }, [state]);
    
      return (
        <div style={{ display: "flex", flexDirection: "column", gap: 12, maxWidth: 320 }}>
          <ProgressBar
            progress={state === "starting" || state === "idle" ? false : progress}
          />
          <Button
            disabled={state === "starting" || state === "running"}
            onClick={() => {
              setProgress(0);
              setState("starting");
              setTimeout(() => setState("running"), 800);
            }}
          >
            Start task
          </Button>
        </div>
      );
    }
    
    export default App;

    API

    PropDescriptionTypeDefault
    progressProgress percentage (0–100, clamped); false is indeterminate (sliding animation), and non-finite values (e.g. NaN) are treated as indeterminate toonumber | falsefalse
    disabledWhether disabledbooleanfalse
    ...restNative div props (className, id, aria-label, etc.) passed straight to the rootHTMLAttributes<HTMLDivElement>—

    The bar is read-only and not focusable; it announces the value through aria-valuenow / aria-valuemin / aria-valuemax. With determinate progress aria-valuenow is the clamped value; with indeterminate progress it is omitted. To give the bar an accessible name, use aria-label.

    See also