Await
Fetching data from a server is asynchronous, so the UI may need to display a loading state while the request is pending.
<Await> provides a boundary for this state. It hides the boundary's content and displays its fallback until the asynchronous data used within the boundary has finished loading.
Async Cells
<Await> works with Async Cells. A normal Cell holds a value. An Async Cell, created using Cell.derivedAsync(), holds a Promise that resolves to a value.
When an async cell is used in a template, Retend detects its pending state and notifies the nearest <Await> ancestor.
Basic Usage
To use an asynchronous boundary, wrap your components in <Await> and provide a fallback element to show while loading.
import { Await, If, Cell } from 'retend'; function UserProfile() { // 1. Create an Async Cell to fetch data const userData = Cell.derivedAsync(async () => { const response = await fetch('https://api.example.com/user'); return response.json(); }); return ( <div class="user-profile"> {/* 2. Because userData is an Async Cell, this If() automatically tells the parent <Await> to show the fallback until the fetch finishes. */} {If(userData, { true: (user) => ( <div> <h2>{user.name}</h2> <p>{user.email}</p> </div> ), })} </div> ); } export function App() { return ( <main> <h1>Dashboard</h1> {/* 3. Wrap everything in an <Await> boundary */} <Await fallback={<p>Loading user data...</p>}> <UserProfile /> </Await> </main> ); }
While the request is pending, <Await> displays Loading user data.... After the request resolves, it removes the fallback and renders UserProfile with the user's name and email.
Waiting for Multiple Things
An <Await> boundary waits for all asynchronous operations inside it to finish before rendering its content.
If DashboardContent fetches the user's profile and a list of recent posts, the <Await> fallback remains visible until both requests finish.
import { Await, If, For, Cell } from 'retend'; function DashboardContent() { const userData = Cell.derivedAsync(async () => fetch('/api/user').then((r) => r.json()) ); const postsData = Cell.derivedAsync(async () => fetch('/api/posts').then((r) => r.json()) ); return ( <div> {/* Retend sees both of these Async Cells and tells the boundary to wait for both! */} {If(userData, (u) => ( <h2>{u.name}'s Dashboard</h2> ))} {For(postsData, (p) => ( <p>{p.title}</p> ))} </div> ); } export function Dashboard() { return ( // This fallback shows until both userData AND postsData are done <Await fallback={<p>Loading dashboard entirely...</p>}> <DashboardContent /> </Await> ); }
To load them independently, wrap the two sections in separate <Await> components.
Server-Side Rendering (Advanced)
For Server-Side Rendering (SSR), <Await> boundaries identify asynchronous work that must resolve before the server sends the HTML response.
Use waitForAsyncBoundaries() in server code to wait for these boundaries to settle.