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-native-auto-shimmer

v0.0.4

Published

Auto-generated skeleton loading screens for React Native

Readme

React Native Auto Shimmer

Stop hand-coding skeleton screens. Capture them from your real UI in one click.

npm version npm downloads license React Native Expo PRs Welcome

⭐ If this saves you time, star the repo — it takes 2 seconds and helps others find it.

Auto Pulse pulse animation

Auto Shimmer shimmer animation

ShimmerOverlay shimmer overlay effect

ShimmerPlaceholder shimmer placeholder effect


The problem with skeleton screens today

Every React Native developer has been here:

🤦 "OK let me eyeball this card width... maybe 340px? Let me check on a different device..."
🤦 "The design changed again. Rebuild all the skeletons."
🤦 "Why does the skeleton not match the real layout on iPad?"

Building skeletons by hand is a time sink that should not exist. react-native-auto-shimmer fixes this permanently.


How it works (30 seconds)

Skeleton Inspector live capture

The Skeleton Inspector DevTools panel — click Capture, get pixel-perfect skeletons instantly

  1. Wrap your component with <SkeletonCapture name="card">
  2. Open Skeleton Inspector in React Native DevTools
  3. Click Capture — real skeleton geometry is measured from the live fiber tree
  4. Delete any unwanted pieces with the trash icon
  5. Save — a TypeScript file lands in src/skeletons/ with a ready-to-paste code snippet
  6. Done — skeletons that match your UI exactly, on every device size

No guessing. No hardcoding. No drift.


Three ways to use it

| | Auto Shimmer ⭐ Primary | ShimmerOverlay | ShimmerPlaceholder | |---|---|---|---| | What | Skeleton from live UI, zero manual work | Shimmer sweep over any element | Fixed-size shimmer box | | Layout | Measured from real component | Wraps whatever you give it | You set width / height | | Responsive | ✅ % widths, any screen | — | ❌ Fixed px | | Setup | One-time DevTools capture | Wrap & go | Drop in | | Use when | Building loading screens | Shine/highlight effects | Simple one-off placeholders |

Start with Auto Shimmer. Drop to the manual options only for quick one-offs or pure visual effects.


Installation

npm install react-native-auto-shimmer
# or
yarn add react-native-auto-shimmer

No native modules · No pod install · No Gradle changes · Works with Expo · React Native ≥ 0.68


Auto Shimmer ⭐

The main event. Capture pixel-perfect skeleton layouts directly from your running UI — no measurement, no guesswork.

Before vs. After

- // 😩 The old way
- const SkeletonCard = () => (
-   <View style={{ width: 340, height: 180, backgroundColor: '#e0e0e0' }} />
-   <View style={{ width: 280, height: 18,  backgroundColor: '#e0e0e0', marginTop: 16 }} />
-   <View style={{ width: 200, height: 14,  backgroundColor: '#e0e0e0', marginTop: 8  }} />
-   // ... and redo all of this every time the design changes
- );

+ // ✅ With react-native-auto-shimmer
+ <Skeleton loading={loading} name="card" style={styles.card}>
+   <ArticleCard />
+ </Skeleton>

Setup

1 — Install dev dependencies

yarn add -D @rozenite/metro react-native-auto-shimmer-rozenite-plugin

2 — Configure Metro

// metro.config.js
const { getDefaultConfig } = require('@expo/metro-config'); // or require('metro-config')
const { withRozenite } = require('@rozenite/metro');
const { withSkeletonInspector } = require('react-native-auto-shimmer-rozenite-plugin/metro');

let config = getDefaultConfig(__dirname);
config = withSkeletonInspector(config);

if (process.env.WITH_ROZENITE === 'true') {
  config = withRozenite(config, { enabled: true });
}

module.exports = config;

3 — Register the plugin

// App.tsx or index.js
if (__DEV__) {
  const { getRozeniteDevToolsClient } = require('@rozenite/plugin-bridge');
  const setupPlugin = require('react-native-auto-shimmer-rozenite-plugin').default;

  getRozeniteDevToolsClient('react-native-auto-shimmer')
    .then((client) => setupPlugin(client))
    .catch((e) => console.warn('[SkeletonInspector] Could not connect:', e?.message));
}

4 — Wrap your component

import { Skeleton, SkeletonCapture } from 'react-native-auto-shimmer';

export function ArticleScreen() {
  const [loading, setLoading] = useState(true);

  return (
    <Skeleton loading={loading} initialSkeletons={cardSkeletons} style={styles.card}>
      {/* Place SkeletonCapture inside Skeleton so it measures visible content */}
      <SkeletonCapture name="card">
        <ArticleCard />
      </SkeletonCapture>
    </Skeleton>
  );
}

5 — Run the inspector

WITH_ROZENITE=true yarn start

Open your app → React Native DevToolsRozenite tabSkeleton Inspector

6 — Capture, review, save

  1. Navigate to your screen (make sure loading is false — real content must be visible)
  2. Click ⬡ Capture — skeletons are measured from the live layout
  3. Delete unwanted pieces with the trash icon
  4. Click ↓ Save .ts (Responsive) — file lands in src/skeletons/
  5. Copy the ready-to-paste snippet from the panel

Skeleton Inspector — captured skeletons

Save .ts vs Save .json .ts stores x/w as percentages → scales to every screen size. Use this. .json stores raw dp values → fastest for a quick snapshot on a single device.


Using your skeletons

Import directly (recommended)

import cardSkeletons from './skeletons/card.skeletons'; // .ts — responsive

<Skeleton loading={loading} initialSkeletons={cardSkeletons} style={styles.card}>
  <ArticleCard />
</Skeleton>

Register once, use everywhere (optional)

// App.tsx
import { registerSkeletons } from 'react-native-auto-shimmer';
import cardSkeletons    from './skeletons/card.skeletons';
import profileSkeletons from './skeletons/profile.skeletons';

registerSkeletons({ card: cardSkeletons, profile: profileSkeletons });
// Any screen — no import needed
<Skeleton name="card" loading={loading} style={styles.card}>
  <ArticleCard />
</Skeleton>

Global config

import { configureSkeleton } from 'react-native-auto-shimmer';

configureSkeleton({
  animate:   'shimmer',           // 'pulse' | 'shimmer' | 'solid'
  color:     'rgba(0,0,0,0.06)',
  darkColor: 'rgba(255,255,255,0.08)',
});

API — <Skeleton>

| Prop | Type | Default | Description | |------|------|---------|-------------| | loading | boolean | required | Show skeleton (true) or real content (false) | | children | ReactNode | required | Your component | | name | string | — | Registry key set via registerSkeletons | | initialSkeletons | SkeletonResult \| ResponsiveSkeletons | — | Direct skeleton data — takes priority over name | | animate | 'pulse' \| 'shimmer' \| 'solid' \| boolean | 'pulse' | Animation style | | color | string | 'rgba(0,0,0,0.08)' | Skeleton colour (light mode) | | darkColor | string | 'rgba(255,255,255,0.06)' | Skeleton colour (dark mode) | | style | ViewStyle | — | Wrapper style — set width, height, borderRadius here | | fallback | ReactNode | — | Shown when loading=true but no skeletons exist yet |

API — <SkeletonCapture> (dev only)

No-op in production — tree-shakes to a plain <View> with zero overhead.

| Prop | Type | Description | |------|------|-------------| | name | string | Identifier shown in Skeleton Inspector | | children | ReactNode | The component to measure | | style | ViewStyle | Forwarded to the wrapper View |


Animation styles

| Value | Behaviour | |-------|-----------| | 'pulse' | Opacity 100% → 45% loop — runs on the native UI thread | | 'shimmer' | Bright highlight sweeps left-to-right across all pieces | | 'solid' | Static — no animation (good for Reduce Motion) |


Responsive skeletons & dark mode

Multi-breakpoint: Capture at each device width. The file stores one entry per breakpoint; <Skeleton> picks the nearest match automatically.

Dark mode: <Skeleton> reads useColorScheme() internally — pass color and darkColor or set them once with configureSkeleton.


Manual Options

Quick placeholders without the capture workflow.


ShimmerOverlay

Adds a shimmer sweep to any existing element — real content, buttons, images, cards. Not a loading placeholder; a visual highlight effect.

import { ShimmerOverlay } from 'react-native-auto-shimmer';

// Basic
<ShimmerOverlay>
  <View style={styles.card} />
</ShimmerOverlay>

// Premium button highlight
<ShimmerOverlay color="rgba(255,200,0,0.7)" mode="expand" angle={20} duration={1800}>
  <PremiumButton />
</ShimmerOverlay>

// Staggered list
{items.map((item, i) => (
  <ShimmerOverlay key={item.id} initialDelay={i * 200}>
    <ListRow item={item} />
  </ShimmerOverlay>
))}

// Programmatic control
const shimmerRef = useRef<ShimmerOverlayRef>(null);
<ShimmerOverlay ref={shimmerRef} active={false}>
  <View style={styles.banner} />
</ShimmerOverlay>
<Button title="Shine" onPress={() => shimmerRef.current?.start()} />

Props

| Prop | Type | Default | Description | |---|---|---|---| | active | boolean | true | Whether the animation runs | | color | string | 'rgba(255,255,255,0.8)' | Shimmer band colour | | duration | number | 1500 | One cycle in ms | | delay | number | 400 | Pause between cycles | | initialDelay | number | 0 | Delay before first cycle | | angle | number | 20 | Band angle in degrees | | bandWidth | number | 60 | Band width in px | | mode | 'normal' \| 'expand' \| 'shrink' | 'normal' | Band size style | | direction | 'left-to-right' \| 'right-to-left' | 'left-to-right' | Sweep direction | | iterations | number | -1 | Cycles (-1 = infinite) | | respectReduceMotion | boolean | true | Pauses on system Reduce Motion | | pauseOnBackground | boolean | true | Pauses when app backgrounds | | onAnimationComplete | () => void | — | Called when all iterations finish |

Ref methods

ref.current?.start();       // Start
ref.current?.stop();        // Stop
ref.current?.restart();     // Restart from beginning
ref.current?.isAnimating(); // → boolean

ShimmerPlaceholder

A fixed-size shimmer box you size in code. Drop-in for react-native-shimmer-placeholder with zero dependencies — no LinearGradient needed.

Migrate in 2 lines

- import LinearGradient from 'react-native-linear-gradient';
- import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder';
- const ShimmerPlaceHolder = createShimmerPlaceholder(LinearGradient);

+ import { createShimmerPlaceholder } from 'react-native-auto-shimmer';
+ const ShimmerPlaceHolder = createShimmerPlaceholder();

Usage

import { ShimmerPlaceholder } from 'react-native-auto-shimmer';

// Text line
<ShimmerPlaceholder width={220} height={14} borderRadius={6} visible={loaded}>
  <Text>{title}</Text>
</ShimmerPlaceholder>

// Avatar circle
<ShimmerPlaceholder width={48} height={48} borderRadius={24} visible={loaded}>
  <Image source={{ uri: avatarUrl }} style={styles.avatar} />
</ShimmerPlaceholder>

// Dark mode
<ShimmerPlaceholder
  width={240} height={16} borderRadius={8}
  shimmerColors={['#2a2a3e', '#3d3d5c', '#2a2a3e']}
  visible={loaded}
>
  <Text>{label}</Text>
</ShimmerPlaceholder>

Full placeholder card

function ArticleCardPlaceholder({ loaded }: { loaded: boolean }) {
  return (
    <View style={styles.card}>
      <ShimmerPlaceholder width={340} height={180} visible={loaded}>
        <Image source={{ uri: imageUrl }} style={styles.hero} />
      </ShimmerPlaceholder>
      <View style={styles.body}>
        <ShimmerPlaceholder width={280} height={18} borderRadius={6} visible={loaded} style={{ marginBottom: 8 }}>
          <Text style={styles.title}>{title}</Text>
        </ShimmerPlaceholder>
        <ShimmerPlaceholder width={300} height={13} borderRadius={6} visible={loaded} style={{ marginBottom: 6 }}>
          <Text>{excerpt}</Text>
        </ShimmerPlaceholder>
        <View style={{ flexDirection: 'row', gap: 10, alignItems: 'center' }}>
          <ShimmerPlaceholder width={32} height={32} borderRadius={16} visible={loaded}>
            <Image source={{ uri: avatarUrl }} style={styles.avatar} />
          </ShimmerPlaceholder>
          <ShimmerPlaceholder width={120} height={13} borderRadius={6} visible={loaded}>
            <Text>{authorName}</Text>
          </ShimmerPlaceholder>
        </View>
      </View>
    </View>
  );
}

Props

| Prop | Type | Default | Description | |---|---|---|---| | width | number | 200 | Width in px | | height | number | 15 | Height in px | | visible | boolean | false | true = show children, false = show shimmer | | shimmerColors | [string, string, string] | ['#ebebeb','#d0d0d0','#ebebeb'] | Gradient colours | | isReversed | boolean | false | Sweep right-to-left | | duration | number | 1000 | One sweep in ms | | delay | number | 0 | Delay before each sweep | | borderRadius | number | 0 | Border radius | | style | StyleProp<ViewStyle> | — | Outer container style | | contentStyle | StyleProp<ViewStyle> | — | Children wrapper style | | shimmerStyle | StyleProp<ViewStyle> | — | Shimmer box style |


FAQ

No — but you can safely leave it. In production (__DEV__ === false) it renders as a transparent <View> with no bridge calls whatsoever.

Navigate to the screen that mounts your <SkeletonCapture>. Components register on mount and deregister on unmount — the panel reflects what's currently on screen.

No skeleton data has been passed yet. Use fallback while you complete the capture:

<Skeleton loading={loading} fallback={<View style={styles.placeholder} />}>
  <MyCard />
</Skeleton>

Capture at every meaningful width. Each capture adds a breakpoint to the same file and <Skeleton> picks the closest one at runtime.

Yes. x/w are % of container width, y/h are dp, r is border-radius in dp or "50%" for circles. Or just delete unwanted pieces in the inspector before saving.

Yes. No native modules. The Rozenite plugin uses Metro's enhanceMiddleware API, which Expo supports out of the box.

Yes. SkeletonCapture detects the renderer at runtime and uses the correct measurement path for both Fabric and the legacy renderer.

Yes. <SkeletonCapture> registers on mount and cleans up on unmount — no conflicts with any navigation library.


Contributing

Bug fixes, features, and docs improvements are all welcome.

Support the project

If this library saves you time, a star on GitHub goes a long way — it helps other developers discover it.

⭐ Star on GitHub


inspired by boneyard

License

MIT © Numan