Ghostly
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>