Guides
Suspense Integration
Using Ghostly with React Suspense, RSC, and streaming SSR.
GhostlySuspense (recommended)
The easiest way to use Ghostly with Suspense. No loading state to manage.
import { GhostlySuspense } from '@ghostly-ui/react'
<GhostlySuspense fallback={<ProductCard />}>
<AsyncProductCard />
</GhostlySuspense>GhostlySuspense wraps React's <Suspense> and automatically renders your fallback inside <Ghostly loading={true}> as the Suspense boundary fallback.
This is the recommended approach for any project using React Server Components, the use() hook, or data-fetching libraries with Suspense support.
With options
<GhostlySuspense
fallback={<UserProfile />}
animation="pulse"
radius="lg"
color="hsl(260 30% 88%)"
smooth
>
<AsyncUserProfile userId={123} />
</GhostlySuspense>Multiple boundaries
<div className="grid grid-cols-12 gap-6">
<div className="col-span-8">
<GhostlySuspense fallback={<Chart />} animation="shimmer">
<AsyncChart />
</GhostlySuspense>
</div>
<div className="col-span-4">
<GhostlySuspense fallback={<Feed />} animation="wave">
<AsyncFeed />
</GhostlySuspense>
</div>
</div>Manual Suspense fallback
If you need more control, use <Ghostly> directly as a Suspense fallback:
import { Suspense } from 'react'
import { Ghostly } from '@ghostly-ui/react'
<Suspense fallback={
<Ghostly loading={true}>
<ProductCard />
</Ghostly>
}>
<AsyncProductCard />
</Suspense>Next.js loading.tsx
import { GhostlyList } from '@ghostly-ui/react'
import { ProductCard } from '@/components/product-card'
export default function Loading() {
return (
<GhostlyList loading={true} count={6} item={<ProductCard />} className="grid grid-cols-3 gap-4">
<></>
</GhostlyList>
)
}Why it works with streaming SSR
Ghostly's CSS-first approach is perfect for streaming:
- CSS loads once in
<head> - Streamed boundaries arrive with
data-ghostlyattribute - When data resolves, the attribute is removed
- No hydration mismatch — attribute is set server-side