Server-Side Rendering
Boneyard is inherently SSR-friendly. The CLI pre-computes your skeleton layout at build time into .bones.json files — so the skeleton is ready to render on the very first frame. No runtime DOM measurement, no layout shift, no waiting for JavaScript to hydrate.
Pass a fixture prop with representative placeholder data to your <Skeleton>. At build time, Playwright renders this fixture, snapshots the real DOM layout, and writes the exact pixel positions to a .bones.json file. At runtime, import that file as initialBones — the skeleton renders instantly from the pre-generated data, no runtime DOM measurement needed.
Real components on the left, their extracted skeletons on the right.
Notification list
import { Skeleton } from "boneyard-js/react"
import bones from "./bones/notifications.bones.json"
// Fixture — representative data for build-time snapshot
const fixture = [
{ name: "Anna Kim", text: "Merged PR #142" },
{ name: "Tom Lee", text: "Commented on review" },
{ name: "Sara R.", text: "Assigned you to INFRA-301" },
]
export default function Page() {
const { data, isLoading } = useFetch("/api/notifications")
return (
<Skeleton
name="notifications"
loading={isLoading}
fixture={fixture}
initialBones={bones}
>
<NotificationList data={data ?? fixture} />
</Skeleton>
)
}Dashboard stats
Feed post
Just shipped the new dashboard. Feels good to see real metrics after weeks of placeholder data.
Just shipped the new dashboard. Feels good to see real metrics after weeks of placeholder data.
- React Server Components — skeleton loads with the page, not after hydration
- Static sites / SSG — bake skeletons into the build output
- Edge functions — render skeletons at the edge with zero runtime cost
- Non-React apps —
renderBonesreturns an HTML string for any template engine
Most skeleton libraries measure the DOM at runtime — which means the skeleton can't render until JavaScript loads and hydrates. Boneyard works differently:
- Bones are pre-computed at build time — the CLI snapshots your real layout and saves it as static JSON
- No runtime DOM measurement —
<Skeleton>reads from the JSON, not fromgetBoundingClientRect - Same API everywhere — use
initialBonesor the registry. Works the same in SSR, SSG, SPAs, and edge functions