Browse guides

Represent content that is still resolving

Give one asynchronous profile screen explicit pending, successful and failed output.

The example waits before resolving the profile so fallback remains visible. Its two actions start a successful run or a rejected run with a fresh suspenseKey.

Table of contents

Main APIs

Suspense
Renders fallback while asynchronous children resolve, then renders the result or error output.
tsx
import { mount } from "valyrian.js";
import { Suspense } from "valyrian.js/suspense";

type Outcome = "success" | "error";

const state = { outcome: "success" as Outcome, run: 0 };

const wait = (milliseconds: number) =>
  new Promise((resolve) => setTimeout(resolve, milliseconds));

async function Profile({ outcome }: { outcome: Outcome }) {
  await wait(700);
  if (outcome === "error") {
    throw new Error("Profile request failed");
  }
  return <p>Loaded profile</p>;
}

function load(outcome: Outcome) {
  state.outcome = outcome;
  state.run += 1;
}

const Screen = () => {
  const suspenseKey = `profile:${state.outcome}:${state.run}`;
  return (
    <main>
      <button onclick={() => load("success")}>Load profile</button>
      <button onclick={() => load("error")}>Load rejected profile</button>
      <Suspense
        suspenseKey={suspenseKey}
        fallback={<p>Loading profile...</p>}
        error={(error) => <p>{String(error)}</p>}
      >
        <Profile outcome={state.outcome} />
      </Suspense>
    </main>
  );
};

mount("body", Screen);

Error

The `error` renderer produces the failure output when an asynchronous child rejects.

Continue the cumulative application

Exact references