From aradotso-trending-skills-37
Generates pixel-perfect skeleton loading screens by snapshotting real DOM using boneyard-js. Wraps React components to show loading placeholders during data fetches.
npx claudepluginhub joshuarweaver/cascade-ai-ml-agents-misc-1 --plugin aradotso-trending-skills-37This skill uses the workspace's default tool permissions.
```markdown
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
---
name: boneyard-skeleton-loading
description: Auto-generated pixel-perfect skeleton loading screens extracted from real DOM using boneyard-js
triggers:
- add skeleton loading to my component
- generate skeleton screens automatically
- show loading placeholder while data fetches
- use boneyard for skeleton UI
- pixel perfect skeleton loading
- auto generate loading skeletons
- wrap component with skeleton loader
- boneyard skeleton setup
---
# Boneyard Skeleton Loading
> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.
Boneyard (`boneyard-js`) generates pixel-perfect skeleton loading screens by snapshotting your real DOM — no manual measurement or hand-tuned placeholders needed.
## How It Works
1. Wrap a component with `<Skeleton name="...">` and a `loading` prop
2. Run `npx boneyard-js build` — it launches your dev server, snapshots the DOM, and writes bones files
3. Import the generated registry once in your app root
4. Every `<Skeleton name="...">` auto-resolves its layout from the registry
## Installation
```bash
npm install boneyard-js
import { Skeleton } from 'boneyard-js/react'
function BlogPage() {
const { data, isLoading } = useFetch('/api/post')
return (
<Skeleton name="blog-card" loading={isLoading}>
{data && <BlogCard data={data} />}
</Skeleton>
)
}
npx boneyard-js build
# or specify your dev server URL
npx boneyard-js build http://localhost:3000
app/layout.tsx)import './bones/registry'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
<Skeleton> Props| Prop | Type | Default | Description |
|---|---|---|---|
name | string | required | Unique identifier matching a built bones file |
loading | boolean | required | When true, renders skeleton; when false, renders children |
color | string | #e0e0e0 | Fill color for bones (hex, rgb, etc.) |
animate | boolean | true | Enable pulse shimmer animation |
snapshotConfig | object | — | Control which elements are captured |
# Auto-detect dev server and build all skeletons
npx boneyard-js build
# Target a specific dev server URL
npx boneyard-js build http://localhost:3000
# Custom breakpoints and output directory
npx boneyard-js build --breakpoints 390,820,1440 --out ./public/bones
# Default breakpoints: 375px, 768px, 1280px
import { Skeleton } from 'boneyard-js/react'
function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data)
setLoading(false)
})
}, [userId])
return (
<Skeleton name="user-profile" loading={loading}>
{user && (
<div className="profile">
<img src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.bio}</p>
</div>
)}
</Skeleton>
)
}
import { Skeleton } from 'boneyard-js/react'
import { useQuery } from '@tanstack/react-query'
function ProductCard({ productId }: { productId: string }) {
const { data, isLoading } = useQuery({
queryKey: ['product', productId],
queryFn: () => fetch(`/api/products/${productId}`).then(r => r.json()),
})
return (
<Skeleton name="product-card" loading={isLoading} color="#f0f0f0">
{data && (
<div className="product">
<img src={data.image} alt={data.title} />
<h3>{data.title}</h3>
<span>${data.price}</span>
</div>
)}
</Skeleton>
)
}
import { Skeleton } from 'boneyard-js/react'
function PostList() {
const { data: posts, isLoading } = usePosts()
// Show multiple skeleton placeholders while loading
if (isLoading) {
return (
<div className="post-list">
{Array.from({ length: 5 }).map((_, i) => (
<Skeleton key={i} name="post-list-item" loading={true}>
{null}
</Skeleton>
))}
</div>
)
}
return (
<div className="post-list">
{posts.map(post => (
<Skeleton key={post.id} name="post-list-item" loading={false}>
<PostItem post={post} />
</Skeleton>
))}
</div>
)
}
<Skeleton
name="dashboard-chart"
loading={isLoading}
color="#d4d4d4"
animate={false}
>
{data && <Chart data={data} />}
</Skeleton>
<Skeleton
name="nav-header"
loading={isLoading}
snapshotConfig={{
exclude: ['button', '[data-no-skeleton]'],
minWidth: 8,
minHeight: 8,
}}
>
{data && <Header data={data} />}
</Skeleton>
bones/
registry.ts ← import this once in app root
blog-card.bones.json
user-profile.bones.json
product-card.bones.json
Each .bones.json stores a flat array of { x, y, w, h, r } rectangles captured at each breakpoint. The <Skeleton> component reads these at runtime and renders matching gray rectangles.
// app/layout.tsx
import '../bones/registry'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My App',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
// pages/_app.tsx
import '../bones/registry'
import type { AppProps } from 'next/app'
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
<Skeleton name="my-new-component" loading={...}> around your componentnpm run dev)npx boneyard-js buildbones/ directoryimport useSWR from 'swr'
import { Skeleton } from 'boneyard-js/react'
const fetcher = (url: string) => fetch(url).then(r => r.json())
function ArticleView({ slug }: { slug: string }) {
const { data, isLoading } = useSWR(`/api/articles/${slug}`, fetcher)
return (
<Skeleton name="article-view" loading={isLoading}>
{data && (
<article>
<h1>{data.title}</h1>
<p className="byline">By {data.author}</p>
<div dangerouslySetInnerHTML={{ __html: data.content }} />
</article>
)}
</Skeleton>
)
}
import { Skeleton } from 'boneyard-js/react'
function Dashboard() {
const { stats, statsLoading } = useStats()
const { chart, chartLoading } = useChartData()
const { activity, activityLoading } = useActivity()
return (
<div className="dashboard">
<Skeleton name="stats-row" loading={statsLoading}>
{stats && <StatsRow stats={stats} />}
</Skeleton>
<Skeleton name="main-chart" loading={chartLoading}>
{chart && <MainChart data={chart} />}
</Skeleton>
<Skeleton name="activity-feed" loading={activityLoading}>
{activity && <ActivityFeed items={activity} />}
</Skeleton>
</div>
)
}
npx boneyard-js build — the bones file for that name hasn't been generated yetname prop exactly matches the one used during buildbones/registry is imported in your app rootnpm run dev)npx boneyard-js build http://localhost:3000npx boneyard-js build after making layout changes--breakpoints to match your app's actual responsive breakpoints
npx boneyard-js build --breakpoints 390,768,1280,1920
./bones relative to project root--out: npx boneyard-js build --out ./src/bones