Guides
Examples
Real-world patterns and common use cases for Ghostly.
Single component
import { Ghostly } from '@ghostly-ui/react'
function UserPage() {
const { data, isLoading } = useQuery('user')
return (
<Ghostly loading={isLoading} smooth>
<UserProfile user={data} />
</Ghostly>
)
}Product grid
import { GhostlyList } from '@ghostly-ui/react'
function ShopPage() {
const { data: products, isLoading } = useQuery('products')
return (
<GhostlyList
loading={isLoading}
count={8}
item={<ProductCard />}
className="grid grid-cols-2 gap-4 md:grid-cols-4"
>
{products?.map(p => <ProductCard key={p.id} product={p} />)}
</GhostlyList>
)
}Suspense-powered (zero state management)
import { GhostlySuspense } from '@ghostly-ui/react'
function ProductPage({ id }: { id: string }) {
return (
<GhostlySuspense fallback={<ProductCard />} animation="shimmer" smooth>
<AsyncProductCard id={id} />
</GhostlySuspense>
)
}Dashboard with multiple sections
function DashboardPage() {
const stats = useQuery('stats')
const orders = useQuery('orders')
const chart = useQuery('chart')
return (
<div className="space-y-8">
<Ghostly loading={stats.isLoading} animation="pulse" smooth>
<StatsRow stats={stats.data} />
</Ghostly>
<Ghostly loading={chart.isLoading} animation="shimmer" smooth>
<RevenueChart data={chart.data} />
</Ghostly>
<GhostlyList
loading={orders.isLoading}
count={5}
item={<OrderRow />}
animation="wave"
as="ul"
className="flex flex-col divide-y"
>
{orders.data?.map(o => <OrderRow key={o.id} order={o} />)}
</GhostlyList>
</div>
)
}Exclude elements
<Ghostly loading={isLoading}>
<div className="flex items-center justify-between p-4 border rounded-lg">
<div>
<h3>{data?.title ?? ''}</h3>
<p>{data?.description ?? ''}</p>
</div>
<button data-ghostly-ignore className="btn-primary">
Share
</button>
</div>
</Ghostly>Custom colors per section
<Ghostly
loading={true}
color="hsl(260 30% 88%)"
shine="hsl(260 30% 94%)"
>
<SpecialSection />
</Ghostly>Control skeleton line count
<Ghostly loading={isLoading}>
<div>
<h2>{data?.title ?? ''}</h2>
<p data-ghostly-lines="1">{data?.subtitle ?? ''}</p>
<p data-ghostly-lines="5">{data?.body ?? ''}</p>
</div>
</Ghostly>Custom image aspect ratios
<Ghostly loading={isLoading}>
<div className="flex gap-4">
<img data-ghostly-ratio="1/1" src={data?.avatar} alt="" className="w-16 h-16 rounded-full" />
<div>
<h3>{data?.name ?? ''}</h3>
<p>{data?.bio ?? ''}</p>
</div>
</div>
</Ghostly>useGhostly for custom skeleton elements
import { useGhostly } from '@ghostly-ui/react'
function CustomWidget() {
const { loading, getGhostlyProps } = useGhostly()
return (
<div {...getGhostlyProps()}>
<canvas className="w-full h-48" />
<p>Chart description</p>
</div>
)
}
// Wrap in Ghostly to activate
<Ghostly loading={isLoading}>
<CustomWidget />
</Ghostly>