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.
- Host: GitHub
- URL: https://github.com/darula-hpp/shimmer-from-structure
- Owner: darula-hpp
- License: mit
- Created: 2026-01-20T18:59:27.000Z (6 days ago)
- Default Branch: main
- Last Pushed: 2026-01-24T22:04:27.000Z (1 day ago)
- Last Synced: 2026-01-25T04:44:16.907Z (1 day ago)
- Topics: developer-experience, react, shimmer, shimmer-effect, skeleton-loading
- Language: TypeScript
- Homepage:
- Size: 5.57 MB
- Stars: 255
- Watchers: 2
- Forks: 6
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
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.

## 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