API Documentation

A deep-dive into react-adaptive-skeleton— how it works, how to configure it, and how to get the best performance out of it.

Step 1 — Render

Wrap your UI in <AdaptiveSkeleton>. While loading, pass template data to your components so the real DOM structure is present and measurable.

Step 2 — Scan

The library recursively traverses the DOM inside the container. It measures every text node, image, and tagged element, capturing their exact position, size, and border radius relative to the container.

Step 3 — Overlay

An absolutely-positioned overlay is drawn on top of the hidden content. Each rectangle is a clone of your skeleton template, sized and placed to pixel-perfectly match the real layout.

The API

The library exports a single factory function called createAdaptiveSkeleton. Use it to create a reusable skeleton component for your application. It requires a React Element that serves as your skeleton "template" and an optional configuration object.

Pro Tip: Create a Reusable ComponentWe recommend creating a single AdaptiveSkeleton component in your UI library and exporting it. This ensures consistent skeleton styling across your entire application.

Layout NoteThe returned component always wraps your content in a <div> (or your custom render element) with position: relative applied automatically so absolute-placed skeletons are positioned correctly. Keep this extra wrapper in mind when structuring flex/grid layouts — you may need to set an explicit className or style to control sizing.

components/ui/adaptive-skeleton.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import React from "react"; import { createAdaptiveSkeleton } from "react-adaptive-skeleton"; // 1. Define your skeleton "stamp" - how each unit looks const Template = () => ( <div className="bg-zinc-200 dark:bg-zinc-800 rounded animate-pulse w-full h-full" /> ); // 2. Create the reusable component with all available options export const AdaptiveSkeleton = createAdaptiveSkeleton(<Template />, { // Optional: Global selectors to always ignore skipSelectors: [".no-sk", "#ignore-me"], // Optional: Custom selectors to explicitly skeletonize targetSelectors: [".skeletonize", "[data-sk]"], }); // Use it anywhere: <AdaptiveSkeleton isLoading={true}>...</AdaptiveSkeleton>

Default Adapted Tags

To provide a seamless experience out of the box, the library automatically identifies and skeletonizes:

TEXTIMGBUTTONSELECTTEXTAREAIFRAMECANVASVIDEOAUDIOSVGINPUT

Any other block elements (like div or section) require explicit skeletonization using selectors or attributes.

Component Props

skeletonClassName: string

Extra class name(s) appended to every rendered skeleton rectangle. Merged with the template element's own className, so the template's classes are always preserved.

skeletonStyle: CSSProperties

Extra inline styles merged into every rendered skeleton rectangle. Applied on top of the template element's own style, so individual properties can be overridden per-instance without redefining the entire template.

Configuration Options

skipSelectors: string[]

Any valid CSS query that the scanner should skip. This includes tag names ("label"), class names ( attribute selectors ("[data-ignore='true']").

skipTextSelectors: string[]

CSS selectors for elements whose text nodes should be ignored. This is useful when you want to skeletonize an element's structure but keep its text visible or untouched.

targetSelectors: string[]

Explicit CSS selectors, tag names, or attribute selectors defining which elements should be skeletonized. This accepts any valid CSS query.

classNameMerger: (...classes: string[]) => string

Custom function used to merge the skeleton template's className with the instance-level skeletonClassName prop. Pass twMerge from tailwind-merge to avoid Tailwind class conflicts (e.g. two bg-* utilities colliding). When omitted, classes are joined with a plain space.

defaultProps: HTMLAttributes & Record<string, unknown>

Default HTML props applied to the wrapper <div> for every instance of this skeleton. Instance-level props (className, style, data attributes, etc.) override these defaults. Useful for adding persistent utility classes (e.g. group/skeleton for Tailwind group variants) or data attributes without repeating them on every usage.

Attribute Selectors

You can use data attributes to precisely control how the scanner handles specific nodes.

data-skeleton

Forces the scanner to treat the element as a block.

data-no-skeleton

Tells the scanner to ignore this element and all its children.

data-no-skeleton-text

Tells the scanner to ignore the text nodes within this element and its descendants, while still processing other child elements.

data-flat-skeleton

Forces the skeleton to be flat. By default, skeletons automatically adapt to an element's custom border-radius (like rounded-2xl) or fallback to the skeletonTemplate's radius for sharp elements.

Hot Swapping Data

For react-adaptive-skeleton to know what shapes to draw, the underlying DOM structures must be rendered. Feeding "template data" to your UI components while loading isn't just about avoiding a React crash—it's how the library measures the physical dimensions, margins, and layouts to generate perfectly matching skeletons!

example.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 // Define safe fallbacks that represent the visual space you need const dummyUser = { name: "Placeholder Name", role: "Admin" }; function UserProfile() { const { data, isLoading } = useUserQuery(); const userData = isLoading ? dummyUser : data; return ( <AdaptiveSkeleton isLoading={isLoading}> {/* The component renders this structure off-screen to measure bounds. */} <div className="flex gap-4 p-4 border rounded-xl"> {/* Target this specific div for skeletonizing */} <div className="size-12 relative aspect-video" // target this node for skeleton data-skeleton // makes skeleton corners flat ignoring templates border radii data-flat-skeleton > <Image src={userData.avatar} fill alt={userData.name} /> </div> <div className="flex-1 space-y-2"> <h3 className="font-bold">{userData.name}</h3> {/* Skip skeletonizing this metadata */} <p className="text-sm text-zinc-500" data-no-skeleton> {userData.role} </p> </div> </div> </AdaptiveSkeleton> );

Polymorphic Containers

AdaptiveSkeleton is polymorphic by nature, meaning it can render as any HTML element using the as prop. This is particularly useful in situations like table rows, where strict HTML nesting rules must be followed.

Efficiency TipWrap the entire tbody to minimize the number of observers. The library will scan all rows and cells within that single container.

UserTable.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function UserTable({ users, isLoading }) { return ( <table className="w-full min-w-120 text-left border-collapse"> <thead> <tr> <th className="p-2 border-b">Name</th> <th className="p-2 border-b">Role</th> <th className="p-2 border-b">Address</th> </tr> </thead> {/* Wrap the entire tbody — render prop keeps valid HTML structure */} <AdaptiveSkeleton render={<tbody />} isLoading={isLoading}> {users.map((user) => ( <tr key={user.id}> <td className="p-2"> <span data-skeleton>{user.name}</span> </td> <td className="p-2 border-b">{user.role}</td> <td className="p-2 border-b">{user.address}</td> </tr> ))} </AdaptiveSkeleton> </table> ); }

Scroll Containers

Because skeleton rectangles are position: absolute overlays placed relative to the AdaptiveSkeleton container, they will not scroll with the content if you put the component outside of a scrollable element. This causes the skeletons to "float" or overflow weirdly when the user scrolls.

Rule of ThumbAlways place the <AdaptiveSkeleton> inside the scroll container, wrapping the scrolling content directly. Make sure the scroll container itself has a defined dimension and position: relative if necessary.

scroll-example.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 // ❌ WRONG: Skeletons will float over the scroll view <AdaptiveSkeleton isLoading={true}> <div className="h-96 overflow-y-auto"> {/* scrolling content */} </div> </AdaptiveSkeleton> // ✅ CORRECT: The overlay will scroll smoothly with the content <div className="h-96 overflow-y-auto"> <AdaptiveSkeleton isLoading={true}> {/* scrolling content */} </AdaptiveSkeleton> </div>

Performance Analysis

Choosing the right placement for your AdaptiveSkeleton container is key to maintaining a smooth 60fps experience. Depending on your UI complexity, you can choose between a Macro (Single Container) or Micro (Per Card) approach.

Single Container (Macro)

  • + Lowest memory overhead (1 observer)
  • + Simplest implementation
  • - Full rescan on any change

Best For

Static lists or simple layouts where cards don't resize independently.

Per Card (Micro)

  • + Prevents long-tasks on main thread
  • + Precise internal resize detection
  • - Higher overhead (N observers)

Best For

Interactive cards, complex layouts, or large dynamic feeds.