0
0
RemixHow-ToBeginner ยท 4 min read

How to Use Await Component in Remix for Async UI

In Remix, use the Await component inside a Suspense boundary to handle promises and show fallback UI while waiting. Wrap your async data with defer() in loaders, then use Await to render the resolved data or handle errors gracefully.
๐Ÿ“

Syntax

The Await component is used inside a Suspense component to handle asynchronous data in Remix. It takes a resolve prop which is a promise, and optionally errorElement to show if the promise rejects.

Basic structure:

  • <Suspense fallback="Loading...">: Shows fallback UI while waiting.
  • <Await resolve={promise} errorElement="<ErrorComponent />">{data => ...}</Await>: Renders children when promise resolves or error UI if it rejects.
tsx
import { Suspense } from 'react';
import { Await } from '@remix-run/react';

function Component({ promise }) {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Await resolve={promise} errorElement={<div>Error loading data</div>}>
        {(data) => <div>{data.someValue}</div>}
      </Await>
    </Suspense>
  );
}
๐Ÿ’ป

Example

This example shows how to use defer() in the loader to return a promise, then use Await in the component to render the data once it resolves. It also shows a loading fallback and error handling.

tsx
import { defer } from '@remix-run/node';
import { useLoaderData, Await } from '@remix-run/react';
import { Suspense } from 'react';

// Simulate async data fetch
function fetchUser() {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ name: 'Alice', age: 30 }), 2000);
  });
}

export async function loader() {
  return defer({ user: fetchUser() });
}

export default function UserProfile() {
  const data = useLoaderData();

  return (
    <div>
      <h1>User Profile</h1>
      <Suspense fallback={<p>Loading user info...</p>}>
        <Await
          resolve={data.user}
          errorElement={<p>Failed to load user data.</p>}
        >
          {(user) => (
            <div>
              <p>Name: {user.name}</p>
              <p>Age: {user.age}</p>
            </div>
          )}
        </Await>
      </Suspense>
    </div>
  );
}
Output
User Profile Loading user info... (for 2 seconds) Name: Alice Age: 30
โš ๏ธ

Common Pitfalls

  • Not wrapping Await inside a Suspense component causes React errors because Await relies on suspense fallback.
  • Passing non-promise values to resolve will render immediately but defeats the purpose of suspense.
  • Forgetting to handle errors with errorElement leads to unhandled promise rejections and broken UI.
tsx
/* Wrong: Await used without Suspense */
import { Await } from '@remix-run/react';

export default function Wrong() {
  const promise = fetch('/api/data').then(res => res.json());
  return (
    <Await resolve={promise}>
      {(data) => <div>{data.value}</div>}
    </Await>
  );
}

/* Right: Await inside Suspense with fallback and errorElement */
import { Suspense } from 'react';
import { Await } from '@remix-run/react';

export default function Right() {
  const promise = fetch('/api/data').then(res => res.json());
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Await resolve={promise} errorElement={<div>Error loading data</div>}>
        {(data) => <div>{data.value}</div>}
      </Await>
    </Suspense>
  );
}
๐Ÿ“Š

Quick Reference

PropDescriptionRequiredType
resolveThe promise to wait for and renderYesPromise
childrenFunction that receives resolved data and returns JSXYes(data) => ReactNode
errorElementJSX to show if promise rejectsNoReactNode
fallbackPassed to Suspense to show while waitingNoReactNode (on Suspense)
โœ…

Key Takeaways

Always wrap Await inside a Suspense component with a fallback UI.
Use defer() in loaders to return promises that Await can handle.
Provide an errorElement to gracefully handle promise rejections.
Pass a promise to resolve prop; non-promises render immediately without suspense.
The children of Await is a function that receives resolved data to render.