Guides
Working with Lists
Skeleton loaders for grids, tables, and dynamic lists.
The Problem
// products is [] during loading → empty grid → no skeletons!
<Ghostly loading={isLoading}>
<div className="grid grid-cols-3 gap-4">
{products.map(p => <ProductCard key={p.id} product={p} />)}
</div>
</Ghostly>Solution: GhostlyList
<GhostlyList
loading={isLoading}
count={6}
item={<ProductCard />}
className="grid grid-cols-3 gap-4"
>
{products.map(p => <ProductCard key={p.id} product={p} />)}
</GhostlyList>Grid layouts
<GhostlyList
loading={isLoading}
count={8}
item={<ProductCard />}
className="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4"
>
{products.map(p => <ProductCard key={p.id} product={p} />)}
</GhostlyList>Make count a multiple of your grid columns for complete rows (e.g., 6 for 3 columns, 8 for 4 columns).
Semantic lists
Use the as prop to render as a <ul> or <ol> instead of a <div>:
<GhostlyList
loading={isLoading}
count={5}
item={<TodoItem />}
as="ul"
className="space-y-2"
>
{todos.map(t => <TodoItem key={t.id} todo={t} />)}
</GhostlyList>Supported as values: div, ul, ol, section, main, aside.
Vertical lists
<GhostlyList
loading={isLoading}
count={5}
item={<OrderRow />}
className="divide-y divide-gray-200"
>
{orders.map(o => <OrderRow key={o.id} order={o} />)}
</GhostlyList>Custom colors per list
<GhostlyList
loading={isLoading}
count={4}
item={<Card />}
color="hsl(200 50% 85%)"
shine="hsl(200 50% 92%)"
className="grid grid-cols-2 gap-4"
>
{items?.map(i => <Card key={i.id} item={i} />)}
</GhostlyList>Mixed content
<div>
<Ghostly loading={isLoading}>
<div className="flex justify-between mb-4">
<h2>{data?.title ?? ''}</h2>
<span>{data?.count ?? ''} items</span>
</div>
</Ghostly>
<GhostlyList loading={isLoading} count={4} item={<ProductCard />} className="grid grid-cols-2 gap-4">
{data?.products.map(p => <ProductCard key={p.id} product={p} />)}
</GhostlyList>
</div>