Skip to main content
The Await component provides a declarative way to handle asynchronous operations in React, managing loading states, errors, and successful data resolution with a clean, composable API.

When to Use

  • Display loading states while data is being fetched
  • Handle errors from async operations gracefully
  • Show different UI based on promise resolution state
  • Avoid manual promise handling with useState and useEffect
  • Integrate with React Suspense and Error Boundaries automatically

Basic Usage

import { Await } from "@zayne-labs/ui-react/common/await";

const fetchUser = async (id: string) => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};

function UserProfile({ userId }: { userId: string }) {
  const userPromise = fetchUser(userId);

  return (
    <Await.Root promise={userPromise}>
      {(user) => (
        <div>
          <h1>{user.name}</h1>
          <p>{user.email}</p>
        </div>
      )}
    </Await.Root>
  );
}

Component API

Await.Root

The root component that manages the promise lifecycle.
promise
Promise<TValue>
required
The promise to await and resolve
children
React.ReactNode | ((result: TValue) => React.ReactNode)
required
Content to render on success. Can be a render function receiving the resolved value
fallback
React.ReactNode
Fallback UI to show while the promise is pending (passed to Suspense)
errorFallback
React.ReactNode | ((props: ErrorFallbackProps) => React.ReactNode)
Fallback UI to show when the promise rejects (passed to ErrorBoundary)
withSuspense
boolean
default:"true"
Whether to wrap children with React Suspense
withErrorBoundary
boolean
default:"true"
Whether to wrap children with ErrorBoundary
asChild
boolean
Merge props into the child element instead of rendering a wrapper

Await.Success

Renders content when the promise resolves successfully.
children
React.ReactNode | ((result: TValue) => React.ReactNode)
required
Content to render. Can be a render function receiving the resolved value

Await.Error

Renders content when the promise rejects.
children
React.ReactNode | ((context: ErrorBoundaryContext) => React.ReactNode)
required
Content to render on error. Render function receives error and resetErrorBoundary
asChild
boolean
Merge props into the child element

Await.Pending

Renders content while the promise is pending.
children
React.ReactNode
required
Loading UI to display

Advanced Examples

Using Inline Props

import { Await } from "@zayne-labs/ui-react/common/await";

function DataDisplay() {
  const dataPromise = fetchData();

  return (
    <Await.Root
      promise={dataPromise}
      fallback={<Spinner />}
      errorFallback={({ error, resetErrorBoundary }) => (
        <div>
          <p>Failed to load: {error.message}</p>
          <button onClick={resetErrorBoundary}>Retry</button>
        </div>
      )}
    >
      {(data) => <pre>{JSON.stringify(data, null, 2)}</pre>}
    </Await.Root>
  );
}

Disabling Suspense/ErrorBoundary

import { Await } from "@zayne-labs/ui-react/common/await";
import { Suspense } from "react";
import { ErrorBoundary } from "@zayne-labs/ui-react/common/await";

function CustomBoundaries() {
  const promise = fetchData();

  return (
    <ErrorBoundary fallback={(props) => <CustomError {...props} />}>
      <Suspense fallback={<CustomLoader />}>
        <Await.Root
          promise={promise}
          withSuspense={false}
          withErrorBoundary={false}
        >
          {(data) => <div>{data.content}</div>}
        </Await.Root>
      </Suspense>
    </ErrorBoundary>
  );
}

Using asChild for Prop Merging

import { Await } from "@zayne-labs/ui-react/common/await";

function StyledError() {
  const promise = fetchData();

  return (
    <Await.Root promise={promise}>
      <Await.Error asChild>
        {({ error, resetErrorBoundary }) => (
          <div className="error-container" role="alert">
            <h2>Something went wrong</h2>
            <p>{error.message}</p>
            <button onClick={resetErrorBoundary}>Try Again</button>
          </div>
        )}
      </Await.Error>

      <Await.Success>
        {(data) => <div>{data.content}</div>}
      </Await.Success>
    </Await.Root>
  );
}

Comparison to Native Patterns

Traditional Approach

function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    setLoading(true);
    fetchUser(userId)
      .then(setUser)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!user) return null;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

With Await Component

function UserProfile({ userId }: { userId: string }) {
  const userPromise = fetchUser(userId);

  return (
    <Await.Root promise={userPromise} fallback={<div>Loading...</div>}>
      {(user) => (
        <div>
          <h1>{user.name}</h1>
          <p>{user.email}</p>
        </div>
      )}
    </Await.Root>
  );
}
The Await component uses React’s use hook internally to unwrap promises, automatically integrating with Suspense boundaries for optimal loading states.

Common Use Cases

Data Fetching with Loading States

function ProductList() {
  const productsPromise = fetch("/api/products").then((r) => r.json());

  return (
    <Await.Root promise={productsPromise}>
      <Await.Pending>
        <div className="grid gap-4">
          {Array.from({ length: 6 }).map((_, i) => (
            <ProductSkeleton key={i} />
          ))}
        </div>
      </Await.Pending>

      <Await.Success>
        {(products) => (
          <div className="grid gap-4">
            {products.map((product) => (
              <ProductCard key={product.id} product={product} />
            ))}
          </div>
        )}
      </Await.Success>
    </Await.Root>
  );
}

Graceful Error Handling

function UserSettings() {
  const settingsPromise = fetchSettings();

  return (
    <Await.Root promise={settingsPromise}>
      <Await.Error>
        {({ error, resetErrorBoundary }) => (
          <div className="error-state">
            <h2>Failed to load settings</h2>
            <p>{error.message}</p>
            <div className="actions">
              <button onClick={resetErrorBoundary}>Retry</button>
              <button onClick={() => window.location.reload()}>
                Reload Page
              </button>
            </div>
          </div>
        )}
      </Await.Error>

      <Await.Success>
        {(settings) => <SettingsForm settings={settings} />}
      </Await.Success>
    </Await.Root>
  );
}

Build docs developers (and LLMs) love