SSR & Next.js
Skelion is designed to work seamlessly with server-side rendering. The Boneyard Pattern ensures zero hydration mismatches in Next.js.
The Problem
Skeleton loaders that measure the DOM break SSR because:
- The server has no DOM to measure
- The initial client render must match the server output
- Layout measurement can only happen after hydration
The Boneyard Pattern
Skelion solves this with a three-phase approach:
- Server render: Static skeleton placeholder with animations disabled
- Client hydration: Identical markup renders (no mismatch)
- Post-hydration:
useEffectenables animations and triggers layout measurement
Usage
Add the ssr prop to enable SSR-safe rendering:
<Skeleton loading={loading} ssr>
<YourComponent />
</Skeleton>
With Preset Variants
<Skeleton loading={loading} variant="card" ssr>
<CardContent />
</Skeleton>
With Auto Mode
<Skeleton loading={loading} ssr>
<ComplexLayout />
</Skeleton>
In auto mode with SSR:
- Server renders a fallback skeleton placeholder
- Client hydrates the same fallback (no mismatch)
- After hydration, Skelion measures the real layout and swaps to accurate skeletons
Next.js App Router
Skelion is fully compatible with the App Router. The library outputs include the "use client" directive, so it works in client components:
app/components/UserCard.tsx
"use client";
import { Skeleton } from "skelion";
import "skelion/styles.css";
export function UserCard({ user, loading }) {
return (
<Skeleton loading={loading} ssr>
<div className="card">
<img src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.bio}</p>
</div>
</Skeleton>
);
}
Next.js Streaming
Skelion works with React Suspense and Next.js streaming. The SSR fallback renders immediately, and the skeleton activates after the component streams in:
app/page.tsx
import { Suspense } from "react";
import { UserCard } from "./components/UserCard";
export default function Page() {
return (
<Suspense fallback={<Skeleton loading={true} variant="card" ssr />}>
<UserCard />
</Suspense>
);
}
CSS Import in Next.js
Import the CSS in your root layout:
app/layout.tsx
import "skelion/styles.css";
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}