03 — Data fetching
use(async fn) returns a resource. Wrap it in <Suspense> for loading UI and <ErrorBoundary> for failures.
tsx
import { use } from 'flexium/core'
import { Suspense, ErrorBoundary } from 'flexium/dom'
type User = { id: number; name: string; email: string }
function UserProfile({ id }: { id: number }) {
const [user] = use(async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return res.json() as Promise<User>
})
// `user` is the resolved value at this point — Suspense pauses render until it's ready
return (
<article>
<h2>{user.name}</h2>
<a href={`mailto:${user.email}`}>{user.email}</a>
</article>
)
}
export function App() {
return (
<ErrorBoundary fallback={(err) => <p style="color:red">Failed: {err.message}</p>}>
<Suspense fallback={<p>Loading user…</p>}>
<UserProfile id={1} />
</Suspense>
</ErrorBoundary>
)
}How it works
use(async fn)runsfn, captures the promise, and registers the component for suspension.- While the promise is pending,
<Suspense fallback={...}>shows the fallback. - When the promise resolves, the component re-renders with the value.
- If the promise rejects,
<ErrorBoundary fallback={(err) => ...}>catches it.
The async function only re-runs when its captured signals change — referential equality is enough.
Reactive fetch on a signal
To re-fetch when an input changes, derive the resource from a signal:
tsx
function UserSearch() {
const [id, setId] = use(1)
const [user] = use(async () => {
// `id` is captured — fetch re-runs when `id` changes
const res = await fetch(`/api/users/${id}`)
return res.json()
})
return (
<div>
<input type="number" value={id} onchange={(e: any) => setId(+e.target.value)} />
<Suspense fallback={<p>Loading…</p>}>
<p>{user.name}</p>
</Suspense>
</div>
)
}Error recovery
<ErrorBoundary> accepts a reset prop or a render-prop fallback that receives the error:
tsx
<ErrorBoundary fallback={(err, reset) => (
<div>
<p>Error: {err.message}</p>
<button onclick={reset}>Try again</button>
</div>
)}>
<UserProfile id={1} />
</ErrorBoundary>API surface used
use(async fn)— resource (Promise-aware signal)<Suspense fallback={...}>— declarative loading state<ErrorBoundary fallback={(err) => ...}>— declarative error state
Next
→ Form validation — controlled inputs, derived state.