npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-starlight-skeleton

v0.1.0

Published

Auto-generated shimmering skeletons for React: measures your real child layout in a layout effect and compiles a pixel-perfect skeleton overlay. SSR-safe, zero runtime dependencies.

Readme

react-starlight-skeleton

Auto-generated shimmering skeletons for React. Instead of hand-building a fake version of every screen, <StarlightSkeleton> renders your real UI invisibly, measures it in a layout effect, and compiles a pixel-perfect overlay of shimmering blocks that mirrors the actual layout — shapes, alignment, distribution, corner radii and all.

  • Zero runtime dependencies — React and ReactDOM are peer dependencies.
  • Measures the real DOM — one component wraps any layout; no skeleton markup to maintain.
  • Shimmer wave — a single injected <style> tag (id-guarded, shared by all instances) drives a moving linear-gradient animation; respects prefers-reduced-motion.
  • Responsive — re-measures on window resize and via ResizeObserver (guarded when unavailable).
  • SSR-safe — works with Next.js App Router (ships "use client"), no useLayoutEffect warnings on the server, graceful fallback bars on the server-rendered first paint.
  • Fully typed — strict TypeScript, ESM + CJS builds, source maps, .d.ts for both formats.

Install

npm install react-starlight-skeleton
# or
pnpm add react-starlight-skeleton
# or
yarn add react-starlight-skeleton

Requires react and react-dom ^18 || ^19.

Quickstart

Vite (or any client-rendered React app)

import { useEffect, useState } from "react";
import { StarlightSkeleton } from "react-starlight-skeleton";

export function ProfileCard() {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    fetchUser().then(setUser);
  }, []);

  const view = user ?? PLACEHOLDER_USER; // representative placeholder content

  return (
    <StarlightSkeleton loading={user === null}>
      <div className="card">
        <img className="avatar" src={view.avatarUrl} alt="" />
        <h3>{view.name}</h3>
        <p>{view.bio}</p>
        <button>Follow</button>
      </div>
    </StarlightSkeleton>
  );
}

While loading is true the card is rendered invisibly, measured, and each leaf (the image, the heading, the paragraph, the button) gets its own shimmer block at exactly its position and size. When loading finishes, the children render as-is — the wrapper disappears entirely.

Next.js (App Router)

The package ships with the "use client" directive, so you can import it directly inside your own client components:

"use client";

import { StarlightSkeleton } from "react-starlight-skeleton";

export function Dashboard({ data, loading }: DashboardProps) {
  return (
    <StarlightSkeleton loading={loading} baseColor="#1f2430" shimmerColor="#2c3345">
      {/* your real dashboard layout */}
    </StarlightSkeleton>
  );
}

On the server-rendered first paint there is no DOM to measure yet, so fallbackRows generic bars render; the measured overlay takes over in the layout effect after hydration, before the browser paints.

A complete runnable demo lives in example/DashboardExample.tsx: a dashboard (header, avatar + text rows, stat-card grid, activity feed) loading real async data through a fake fetch, with a reload button to replay the skeleton.

How measurement works

  1. While loading is true, your children are still rendered — inside a container with visibility: hidden and aria-hidden, within a position: relative wrapper marked aria-busy.
  2. In a layout effect (before paint), collectSkeletonBlocks(root) walks the hidden subtree and picks leaf visual elements:
    • elements with no child elements;
    • img, svg, button, input, textarea, select, video, canvas and friends — always one block, even with children inside;
    • elements with a direct text node (so <p>Hi <strong>there</strong></p> becomes a single text bar, not two).
    • display: none, visibility: hidden, [hidden] and zero-size nodes are skipped.
  3. Each leaf's getBoundingClientRect() (relative to the wrapper) and computed border-radius become a SkeletonBlock, rendered as an absolutely positioned shimmer <div> in an overlay.
  4. Measurement re-runs on window resize, on ResizeObserver notifications, and whenever children change.
  5. If measurement produces zero blocks (SSR first paint, empty children), fallbackRows generic bars render instead.

Because the skeleton is compiled from the real layout, the quality of the skeleton equals the quality of your placeholder content: render your layout with representative placeholder data while loading (see the example) and the skeleton will match the loaded UI block-for-block.

Props

<StarlightSkeleton>

| Prop | Type | Default | Description | | ------------------- | ------------------ | ----------- | ---------------------------------------------------------------------------------------------------- | | loading | boolean | — required | true shows the measured skeleton overlay; false renders children as-is. | | children | ReactNode | — required | The real UI. Kept mounted (hidden) while loading so it can be measured. | | shimmerColor | string | "#f6f7fb" | Highlight color the wave sweeps with. | | baseColor | string | "#e8eaf0" | Resting color of the blocks. | | animationDuration | number | 1.4 | Seconds per shimmer sweep. | | borderRadius | number \| string | 8 | Radius for blocks whose source element has none, and for fallback bars. Numbers are pixels. | | className | string | — | Class for the outer wrapper while loading. | | style | CSSProperties | — | Styles merged onto the outer wrapper while loading. | | fallbackRows | number | 3 | Generic bars shown when measurement yields zero blocks (SSR first paint, empty children). |

<Bone>

A single hand-placed shimmer block for composing skeletons manually.

| Prop | Type | Default | | ------------------- | ------------------ | ----------- | | width | number \| string | "100%" | | height | number \| string | 16 | | radius | number \| string | 8 | | baseColor | string | "#e8eaf0" | | shimmerColor | string | "#f6f7fb" | | animationDuration | number | 1.4 | | className | string | — | | style | CSSProperties | — |

<Bone width={220} height={24} radius={6} />

Also exported

  • collectSkeletonBlocks(root: HTMLElement): SkeletonBlock[] — the measuring walker, exported for testing and advanced use.
  • ensureShimmerStyles(doc?), STYLE_ELEMENT_ID, SHIMMER_BLOCK_CLASS, WAVE_KEYFRAMES_NAME, shimmerGradient(base, shimmer).
  • useIsomorphicLayoutEffectuseLayoutEffect in the browser, useEffect on the server.
  • Types: StarlightSkeletonProps, SkeletonBlock, BoneProps, plus the DEFAULT_* constants.

SSR notes and limitations

  • Server render: there is no DOM on the server, so nothing can be measured. While loading is true, the server (and the client's first hydration render) outputs the hidden children plus fallbackRows generic bars — markup is identical on both sides, so there is no hydration mismatch. The measured overlay replaces the bars in a layout effect before the first browser paint.
  • The children must render while loading. The skeleton mirrors whatever layout the hidden children produce. If your components render null until data arrives, there is nothing to measure — feed them placeholder data (see the example) or accept the fallback bars.
  • Style injection happens once per document via an id-guarded <style> tag created in an effect — never during server rendering.
  • Animations are disabled under prefers-reduced-motion: reduce.
  • Blocks are absolutely positioned snapshots; layout changes are tracked via resize/ResizeObserver, but continuous animations inside children are not followed frame-by-frame.

Why not hand-rolled skeletons?

Hand-written skeletons are a second implementation of every screen: they drift the moment the real layout changes, they multiply across breakpoints, and they are usually built from guessed pixel sizes. react-starlight-skeleton derives the skeleton from the same JSX that renders the data, so:

  • one source of truth — layout changes update the skeleton automatically;
  • exact geometry — blocks come from getBoundingClientRect, not guesses;
  • responsive for free — the hidden children reflow, the overlay re-measures.

Hand-rolled skeletons (or the <Bone> primitive) still make sense when you cannot render your layout before data arrives, or when you want a deliberately simplified placeholder.

License

MIT © 2026 Dinesh Kumar