The Standalone startTransition

useTransition() is a Hook, so it can only be called inside a component or a custom Hook. React also exports a plain startTransition function (not a Hook) that schedules a Transition the exact same way, for use anywhere a Hook isn't allowed – inside a data library, an event listener set up outside React, or any other plain function. The tradeoff: without a Hook call site of its own, it has nowhere to report back an isPending flag.

Both buttons update the very same counter, wrapped as a Transition either way, so both take about as long to actually apply. Only the button that goes through the Hook ever shows a pending indicator: isPending reflects Transitions started through this component's own useTransition() call specifically, not just any Transition happening anywhere.
RESETRUNFULL
<!DOCTYPE html><html><head>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head><body>
<div></div>
<script type="text/babel">
function SlowValue({ n }) {
  const items = [];
  for (let i = 0; i < 300; i++) {
    const start = performance.now();
    while (performance.now() - start < 1) {} // simulate expensive work
    items.push(<li key={i}>{n} #{i}</li>);
  }
  return <ul>{items}</ul>;
}
function App() {
  const [n, setN] = React.useState(0);
  const [isPending, startTransition] = React.useTransition();
  function incrementViaHook() {
    startTransition(() => setN(n + 1));
  }
  function incrementViaStandalone() {
    // Imagine this running outside any component or Hook (eg. inside a
    // WebSocket handler that lives in a plain data module) -- useTransition
    // itself wouldn't be callable there, but the standalone function is.
    React.startTransition(() => setN(n + 1));
  }
  return (
    <React.Fragment>
      <button onClick={incrementViaHook}>Hook: {n}{isPending ? ' (pending...)' : ''}</button>
      <button onClick={incrementViaStandalone}>Standalone: {n}</button>
      <SlowValue n={n} />
    </React.Fragment>
  );}
const root = ReactDOM.createRoot(document.querySelector("div"));
root.render(<App/>);
</script>
</body></html>

In real code, a function living outside a component wouldn't have setN in scope like this – it would more likely reach it through a module-level reference, a ref, or a store. The inline closure above is simplified for the demo; the scheduling behavior is exactly what you'd see in that more realistic setup.