An open API service indexing awesome lists of open source software.

https://github.com/darula-hpp/shimmer-from-structure

A React library that auto-generates shimmer skeletons from your actual component structure. Zero maintenance, pixel-perfect accuracy.
https://github.com/darula-hpp/shimmer-from-structure

developer-experience react shimmer shimmer-effect skeleton-loading

Last synced: about 18 hours ago
JSON representation

A React library that auto-generates shimmer skeletons from your actual component structure. Zero maintenance, pixel-perfect accuracy.

Awesome Lists containing this project

README

          

# ✨ Shimmer From Structure

A React shimmer/skeleton library that **automatically adapts to your component's runtime structure**. Unlike traditional shimmer libraries that require pre-defined skeleton structures, this library analyzes your actual component's DOM at runtime and generates a shimmer effect that perfectly matches its layout.

![Shimmer From Structure Demo](./example/preview.gif)

## Why This Library?

Traditional shimmer libraries require you to:
- Manually create skeleton components that mirror your real components
- Maintain two versions of each component (real + skeleton)
- Update skeletons every time your layout changes

**Shimmer From Structure** eliminates all of that:
- ✅ Automatically measures your component's structure at runtime
- ✅ Generates shimmer effects that match actual dimensions
- ✅ Zero maintenance - works with any layout changes
- ✅ Works with complex nested structures
- ✅ Supports dynamic data with `templateProps`
- ✅ Preserves container backgrounds during loading
- ✅ Auto-detects border-radius from your CSS

## Installation

```bash
npm install shimmer-from-structure
# or
yarn add shimmer-from-structure
# or
pnpm add shimmer-from-structure
```

# 📖 Basic Usage

### Static Content

For components with hardcoded/static content:

```tsx
import { Shimmer } from 'shimmer-from-structure';

function UserCard() {
return (



John Doe


Software Engineer




);
}
```

### Dynamic Content with `templateProps`

For components that receive dynamic data via props, use `templateProps` to provide mock data for skeleton generation:

```tsx
import { Shimmer } from 'shimmer-from-structure';

// Your component that accepts props
const UserCard = ({ user }) => (



{user.name}


{user.role}



);

// Template data for the skeleton
const userTemplate = {
name: 'Loading...',
role: 'Loading role...',
avatar: 'placeholder.jpg',
};

function App() {
const [loading, setLoading] = useState(true);
const [user, setUser] = useState(null);

return (



);
}
```

The `templateProps` object is spread onto the first child component when loading, allowing it to render with mock data for measurement.

## 🎨 API Reference

### `` Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `loading` | `boolean` | `true` | Whether to show shimmer effect or actual content |
| `children` | `React.ReactNode` | required | The content to render/measure |
| `shimmerColor` | `string` | `'rgba(255,255,255,0.15)'` | Color of the shimmer wave |
| `backgroundColor` | `string` | `'rgba(255,255,255,0.08)'` | Background color of shimmer blocks |
| `duration` | `number` | `1.5` | Animation duration in seconds |
| `fallbackBorderRadius` | `number` | `4` | Border radius (px) for elements with no CSS border-radius |
| `templateProps` | `Record` | - | Props to inject into first child for skeleton rendering |

### Example with All Props

```tsx

```

## 🔧 How It Works

1. **Visible Container Rendering**: When `loading={true}`, your component renders with transparent text but **visible container backgrounds**
2. **Template Props Injection**: If `templateProps` is provided, it's spread onto the first child so dynamic components can render
3. **DOM Measurement**: Uses `useLayoutEffect` to synchronously measure all leaf elements via `getBoundingClientRect()`
4. **Border Radius Detection**: Automatically captures each element's computed `border-radius` from CSS
5. **Shimmer Generation**: Creates absolutely-positioned shimmer blocks matching measured dimensions
6. **Animation**: Applies smooth gradient animation that sweeps across each block

### Key Features

- **Container backgrounds visible**: Unlike `opacity: 0`, we use `color: transparent` so card backgrounds/borders show during loading
- **Auto border-radius**: Circular avatars get circular shimmer blocks automatically
- **Fallback radius**: Text elements (which have `border-radius: 0`) use `fallbackBorderRadius` to avoid sharp rectangles
- **Dark-mode friendly**: Default colors use semi-transparent whites that work on any background

## Examples

### Dashboard with Multiple Sections

Each section can have its own independent loading state:

```tsx
function Dashboard() {
const [loadingUser, setLoadingUser] = useState(true);
const [loadingStats, setLoadingStats] = useState(true);

return (
<>
{/* User profile section */}




{/* Stats section - with custom colors */}



>
);
}
```

### Transactions List

```tsx

```

### Team Members Grid

```tsx

```

## 🔄 Using with React Suspense

Shimmer works seamlessly as a Suspense fallback. When used this way, `loading` is always `true` because React automatically unmounts the fallback and replaces it with the resolved component.

### Basic Suspense Pattern

```tsx
import { Suspense, lazy } from 'react';
import { Shimmer } from 'shimmer-from-structure';

const UserProfile = lazy(() => import('./UserProfile'));

function App() {
return (



}
>


);
}
```

### Why `loading={true}` is Always Set

When using Shimmer as a Suspense fallback:
1. **Suspend**: React renders the fallback → Shimmer shows with `loading={true}`
2. **Resolve**: React **replaces** the entire fallback with the real component
3. The Shimmer is **unmounted**, not updated — so you never need to toggle `loading`

### Performance Tips for Suspense

**Memoize the fallback** to prevent re-renders:
```tsx
const ShimmerFallback = React.memo(() => (



));

// Usage
}>

```

**Keep templates lightweight** — the DOM is measured synchronously via `useLayoutEffect`, so avoid complex logic in your template.

## Global Configuration (Context API)

You can set default configuration for your entire app (or specific sections) using `ShimmerProvider`. This is perfect for maintaining consistent themes without repeating props.

```tsx
import { Shimmer, ShimmerProvider } from 'shimmer-from-structure';

function App() {
return (
// Set global defaults



);
}
```

Components inside the provider automatically inherit values. You can still override them locally:

```tsx
// Inherits blue theme from provider

// Overrides provider settings

```

### Accessing Config in Hooks

If you need to access the current configuration in your own components:

```tsx
import { useShimmerConfig } from 'shimmer-from-structure';

function MyComponent() {
const config = useShimmerConfig();
return

...
;
}
```

## Best Practices

### 1. Use `templateProps` for Dynamic Data
When your component receives data via props, always provide `templateProps` with mock data that matches the expected structure.

### 2. Match Template Structure to Real Data
Ensure your template data has the same array length and property structure as real data for accurate shimmer layout.

### 3. Use Individual Shimmer Components
Wrap each section in its own Shimmer for independent loading states:

```tsx
// ✅ Good - independent loading

// ❌ Avoid - all-or-nothing loading


```

### 4. Consider Element Widths
Block elements like `

`, `

` take full container width. If you want shimmer to match text width:
```css
.title {
width: fit-content;
}
```

### 5. Provide Container Dimensions
For async components (like charts), ensure containers have explicit dimensions so shimmer has something to measure.

## ⚡ Performance Considerations

- Measurement happens only when `loading` changes to `true`
- Uses `useLayoutEffect` for synchronous measurement (no flicker)
- Minimal re-renders - only updates when loading state or children change
- Lightweight DOM measurements using native browser APIs

## 🛠️ Development

```bash
# Install dependencies
npm install

# Run development server with example
npm run dev

# Build library
npm run build
```

## 📝 License

MIT

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## 🐛 Known Limitations

- **Async components**: Components that render asynchronously (like charts using `ResponsiveContainer`) may need explicit container dimensions
- **Zero-dimension elements**: Elements with `display: none` or zero dimensions won't be captured
- **SVG internals**: Only the outer `` element is captured, not internal paths/shapes

## 🚧 Roadmap

- [x] Dynamic data support via `templateProps`
- [x] Auto border-radius detection
- [x] Container background visibility
- [ ] Better async component support
- [ ] Customizable shimmer direction (vertical, diagonal)
- [ ] React Native support
- [ ] Vue.js adapter

---

Made with ❤️ for developers tired of maintaining skeleton screens