Use boneyard-js to add, configure, debug, or rebuild skeleton screens. Triggers when working with Skeleton components, bones JSON, the boneyard CLI, fixtures, leafTags, snapshotConfig, skeleton loading states, or boneyard.config.json.
You are an expert on boneyard-js, a skeleton screen generator that snapshots real UI into positioned rectangle "bones". Use this knowledge to help with any boneyard-related task.
packages/boneyard/)src/react.tsx — <Skeleton> React component (also exports configureBoneyard, registerBones)src/preact.tsx — Native Preact integration (no compat needed)src/Skeleton.svelte — Svelte 5 componentsrc/Skeleton.vue — Vue componentsrc/angular.ts — Angular componentsrc/native.tsx / src/react-native.tsx — React Nativesrc/extract.ts — snapshotBones() DOM walker, fromElement() descriptor extractorsrc/shared.ts — bone registry, animation constants (SHIMMER, PULSE, DEFAULTS), resolveResponsivesrc/types.ts — SnapshotConfig, Bone, CompactBone, ResponsiveBonessrc/runtime.ts — vanilla renderBones() for non-React usagebin/cli.js — CLI entry (boneyard-js build)boneyard-js — snapshotBones, renderBones, fromElementboneyard-js/react — Skeleton, registerBones, configureBoneyardboneyard-js/preact — Skeleton, registerBones, configureBoneyardboneyard-js/native — Skeleton, registerBones, configureBoneyard (React Native)boneyard-js/svelte — Skeleton component, registerBonesboneyard-js/vue — Skeleton component, registerBones, configureBoneyardboneyard-js/angular — SkeletonComponent, registerBones, configureBoneyardboneyard-js/vite — boneyardPlugin() Vite pluginCompact array: [x%, y_px, w%, h_px, borderRadius, isContainer?]
x and w are percentages of container widthy and h are pixelsborderRadius is number (px) or string ("50%")isContainer (optional 6th element, truthy) — container bones are skipped during rendering. They represent parent backgrounds and would cause opacity overlap if rendered alongside child bones.initialBones prop (highest priority)name (from registry.js)window.__BONEYARD_BUILD === true)shared.ts)All frameworks import from shared.ts — single source of truth:
SHIMMER = { angle: 110, start: 30, end: 70, speed: '2s', lightHighlight: '#f7f7f7', darkHighlight: '#2c2c2c' }
PULSE = { speed: '1.8s', lightAdjust: 0.3, darkAdjust: 0.02 }
DEFAULTS = { web: { light: '#f0f0f0', dark: '#222222' }, native: { light: '#f0f0f0', dark: '#222222' } }
Detected via .dark class on <html> or any parent element (standard Tailwind convention). Does NOT use prefers-color-scheme — gives the app developer explicit control. When .dark is present, darkColor and darkShimmerColor are used.
{
leafTags?: string[] // Tags treated as atomic bones (merged with defaults: p,h1-h6,li,td,th)
captureRoundedBorders?: boolean // Capture bordered+rounded elements even without bg (default: true)
excludeTags?: string[] // Skip these tags entirely
excludeSelectors?: string[] // Skip elements matching CSS selectors
}
boneyard.config.json)The primary customization point. Controls both CLI build and runtime defaults. Runtime options are baked into the generated registry.js via configureBoneyard().
{
"breakpoints": [375, 768, 1280],
"out": "./src/bones",
"wait": 800,
"color": "#e5e5e5",
"darkColor": "#2a2a2a",
"animate": "shimmer",
"shimmerColor": "#ebebeb",
"darkShimmerColor": "#333333",
"speed": "2s",
"shimmerAngle": 110,
"stagger": false,
"transition": false,
"boneClass": "",
"resolveEnvVars": true,
"auth": {
"cookies": [{ "name": "session", "value": "env[SESSION_TOKEN]", "domain": "localhost" }],
"headers": { "Authorization": "Bearer env[API_TOKEN]" }
}
}
| Key | Default | Description |
|---|---|---|
| breakpoints | [375, 768, 1280] | Viewport widths captured by CLI (auto-detects Tailwind) |
| out | ./src/bones | Output directory |
| wait | 800 | ms to wait after page load before capturing |
| Key | Default | Description |
|---|---|---|
| color | #f0f0f0 | Bone fill color (light mode) |
| darkColor | #222222 | Bone fill color (dark mode, .dark class) |
| animate | "pulse" | Animation: "pulse", "shimmer", or "solid" |
| shimmerColor | #f7f7f7 | Shimmer highlight color (light mode) |
| darkShimmerColor | #2c2c2c | Shimmer highlight color (dark mode) |
| speed | "2s" (shimmer) / "1.8s" (pulse) | Animation duration |
| shimmerAngle | 110 | Shimmer gradient angle in degrees |
| stagger | false | Delay between bones in ms (true = 80ms) |
| transition | false | Fade transition when loading ends in ms (true = 300ms) |
| boneClass | — | CSS class applied to each bone element |
Per-component props > config file (via configureBoneyard()) > package defaults in shared.ts. CLI flags override config file for build options.
import { Skeleton } from 'boneyard-js/react'
<Skeleton name="my-component" loading={isLoading}>
<MyComponent data={data} />
</Skeleton>
<Skeleton
name="my-component"
loading={isLoading}
fixture={<MyFixture />}
snapshotConfig={{ leafTags: ["section"] }}
>
<MyComponent data={data} />
</Skeleton>
Key pattern: use <section> (or any custom tag) as leaf elements in the fixture, then add that tag to leafTags so the extractor treats each as a single flat bone without recursing into children.
<nav data-no-skeleton>
{/* No bone will be generated */}
</nav>
Or via snapshotConfig:
<Skeleton snapshotConfig={{ excludeSelectors: ['.icon', 'svg'], excludeTags: ['nav'] }}>
# Auto-detect dev server
npx boneyard-js build
# Explicit URL + output
npx boneyard-js build http://localhost:PORT --out src/bones
# Force rebuild all (skip hash check)
npx boneyard-js build --force
# Watch mode (re-capture on HMR)
npx boneyard-js build --watch
# Custom breakpoints
npx boneyard-js build --breakpoints 375,640,768,1024,1280,1536
# React Native mode
npx boneyard-js build --native --out ./bones
| Prop | Type | Default | Description |
|---|---|---|---|
loading | boolean | required | Show skeleton vs children |
children | ReactNode | required | Real content |
name | string | required | Registry key + CLI identifier |
initialBones | ResponsiveBones | — | Pre-generated bones (overrides registry) |
color | string | #f0f0f0 | Bone fill color (light mode) — any CSS color (hex, rgba, hsl, etc.) |
darkColor | string | #222222 | Dark mode bone fill color (.dark class) — any CSS color |
animate | AnimationStyle | 'pulse' | "pulse", "shimmer", "solid" (also accepts boolean) |
stagger | number | boolean | false | Stagger delay (true=80ms) |
transition | number | boolean | false | Fade-out duration (true=300ms) |
boneClass | string | — | CSS class per bone |
className | string | — | Container class |
fallback | ReactNode | — | Shown when loading + no bones |
fixture | ReactNode | — | Mock content for CLI capture |
snapshotConfig | SnapshotConfig | — | Controls bone extraction |
registry.js is imported in app entry AND bones JSON exists for that nameleafTags to snapshotConfig and rebuild--force to regenerate from current DOM<= breakpoint.dark class on <html> or ancestor. Does NOT use prefers-color-schemeloading={false} (or a fixture) so the real UI renders for capturec: true) should be skipped in rendering — if they're not, the filter is missingshimmerColor has enough contrast with color — defaults are #f7f7f7 on #f0f0f0