Description:
Shimmer From Structure is a skeleton loader library that generates shimmer effects by analyzing your component’s runtime DOM structure. It scans your actual component layout and creates matching skeleton blocks automatically.
The library supports React, Vue, and Svelte through dedicated framework adapters. Each adapter hooks into the component lifecycle to measure visible elements and generate shimmer overlays that match their dimensions, positions, and border radii.
The measurement happens during the loading state, and the library produces shimmer animations that sweep across each measured element.
Features
- 🎯 Runtime Structure Analysis: Measures your component’s DOM during render time and generates shimmer effects based on actual element dimensions.
- ⚙️ Zero Maintenance: Adapts to layout changes automatically because it measures the live component structure.
- 🎨 CSS Border Radius Detection: Reads computed border-radius values from your styles and applies them to shimmer blocks.
- 📦 Template Props System: Injects mock data into dynamic components during loading so the library can measure a realistic layout.
- 🖼️ Container Background Preservation: Keeps container backgrounds visible during loading by making text transparent instead of hiding the entire element.
- 🔄 Nested Structure Support: Handles complex component hierarchies by measuring all leaf elements recursively.
See It In Action
How to Use It
Installation
Install the library through npm, yarn, or pnpm.
npm install shimmer-from-structureyarn add shimmer-from-structurepnpm add shimmer-from-structureReact Setup
Import the Shimmer component from the main package for React projects. The React adapter is included in the base package for backward compatibility.
import { Shimmer } from 'shimmer-from-structure';You can also import from the dedicated React package if you prefer explicit framework dependencies.
import { Shimmer } from '@shimmer-from-structure/react';Vue 3 Setup
Import the Shimmer component from the Vue-specific adapter package. Vue 3 requires this separate import path.
import { Shimmer } from '@shimmer-from-structure/vue';Svelte Setup
Import from the Svelte adapter package for Svelte projects.
import { Shimmer } from '@shimmer-from-structure/svelte';Static Content Implementation
For components with hardcoded content, wrap your component in the Shimmer wrapper and control the loading state through the loading prop.
React Static Example
import { Shimmer } from 'shimmer-from-structure';
function UserCard() {
return (
<Shimmer loading={isLoading}>
<div className="card">
<img src="avatar.jpg" className="avatar" />
<h2>John Doe</h2>
<p>Software Engineer</p>
</div>
</Shimmer>
);
}Vue Static Example
<script setup>
import { ref } from 'vue';
import { Shimmer } from '@shimmer-from-structure/vue';
const isLoading = ref(true);
</script>
<template>
<Shimmer :loading="isLoading">
<div class="card">
<img src="avatar.jpg" class="avatar" />
<h2>John Doe</h2>
<p>Software Engineer</p>
</div>
</Shimmer>
</template>Svelte Static Example
<script>
import { Shimmer } from '@shimmer-from-structure/svelte';
let isLoading = true;
</script>
<Shimmer loading={isLoading}>
<div class="card">
<img src="avatar.jpg" class="avatar" />
<h2>John Doe</h2>
<p>Software Engineer</p>
</div>
</Shimmer>Dynamic Content with Template Props
Components that receive data through props need the templateProps option. This prop contains mock data that the library spreads onto your component during loading, which creates a measurable structure.
React Dynamic Example
Define your component that accepts data props, then create a template object with placeholder values. Pass this template both to the Shimmer component and as fallback data to your component.
import { Shimmer } from 'shimmer-from-structure';
const UserCard = ({ user }) => (
<div className="card">
<img src={user.avatar} className="avatar" />
<h2>{user.name}</h2>
<p>{user.role}</p>
</div>
);
const userTemplate = {
name: 'Loading...',
role: 'Loading role...',
avatar: 'placeholder.jpg',
};
function App() {
const [loading, setLoading] = useState(true);
const [user, setUser] = useState(null);
return (
<Shimmer loading={loading} templateProps={{ user: userTemplate }}>
<UserCard user={user || userTemplate} />
</Shimmer>
);
}Vue Dynamic Example
<script setup>
import { ref } from 'vue';
import { Shimmer } from '@shimmer-from-structure/vue';
import UserCard from './UserCard.vue';
const loading = ref(true);
const userTemplate = {
name: 'Loading...',
role: 'Loading role...',
avatar: 'placeholder.jpg',
};
</script>
<template>
<Shimmer :loading="loading" :templateProps="{ user: userTemplate }">
<UserCard :user="user || userTemplate" />
</Shimmer>
</template>Svelte Dynamic Example
<script>
import { Shimmer } from '@shimmer-from-structure/svelte';
import UserCard from './UserCard.svelte';
export let user;
let loading = true;
const userTemplate = {
name: 'Loading...',
role: 'Loading role...',
avatar: 'placeholder.jpg',
};
</script>
<Shimmer loading={loading} templateProps={{ user: userTemplate }}>
<UserCard user={user || userTemplate} />
</Shimmer>The library spreads the templateProps object onto the first child component when loading is true. This allows your component to render with realistic structure that the library can measure.
Dashboard Implementation
Create independent loading states for different sections by wrapping each section in its own Shimmer component.
React Dashboard
function Dashboard() {
const [loadingUser, setLoadingUser] = useState(true);
const [loadingStats, setLoadingStats] = useState(true);
return (
<>
<Shimmer loading={loadingUser} templateProps={{ user: userTemplate }}>
<UserProfile user={user} />
</Shimmer>
<Shimmer
loading={loadingStats}
templateProps={{ stats: statsTemplate }}
shimmerColor="rgba(20, 184, 166, 0.2)"
>
<StatsGrid stats={stats} />
</Shimmer>
</>
);
}Vue Dashboard
<template>
<Shimmer :loading="loadingUser" :templateProps="{ user: userTemplate }">
<UserProfile :user="user" />
</Shimmer>
<Shimmer
:loading="loadingStats"
:templateProps="{ stats: statsTemplate }"
shimmerColor="rgba(20, 184, 166, 0.2)"
>
<StatsGrid :stats="stats" />
</Shimmer>
</template>Svelte Dashboard
<Shimmer loading={loadingUser} templateProps={{ user: userTemplate }}>
<UserProfile {user} />
</Shimmer>
<Shimmer
loading={loadingStats}
templateProps={{ stats: statsTemplate }}
shimmerColor="rgba(20, 184, 166, 0.2)"
>
<StatsGrid {stats} />
</Shimmer>Transactions List Implementation
React
<Shimmer loading={loadingTransactions} templateProps={{ transactions: transactionsTemplate }}>
<TransactionsList transactions={transactions} />
</Shimmer>Vue
<Shimmer :loading="loadingTransactions" :templateProps="{ transactions: transactionsTemplate }">
<TransactionsList :transactions="transactions" />
</Shimmer>Svelte
<Shimmer loading={loadingTransactions} templateProps={{ transactions: transactionsTemplate }}>
<TransactionsList {transactions} />
</Shimmer>Team Members Grid Implementation
React
<Shimmer loading={loadingTeam} templateProps={{ members: teamTemplate }}>
<TeamMembers members={team} />
</Shimmer>Vue
<Shimmer :loading="loadingTeam" :templateProps="{ members: teamTemplate }">
<TeamMembers :members="team" />
</Shimmer>Svelte
<Shimmer loading={loadingTeam} templateProps={{ members: teamTemplate }}>
<TeamMembers members={team} />
</Shimmer>React Suspense Integration
The library works as a Suspense fallback component. When you use it this way, the loading prop stays true because React unmounts the fallback and replaces it with the resolved component.
Basic Suspense Pattern
import { Suspense, lazy } from 'react';
import { Shimmer } from 'shimmer-from-structure';
const UserProfile = lazy(() => import('./UserProfile'));
function App() {
return (
<Suspense
fallback={
<Shimmer loading={true} templateProps={{ user: userTemplate }}>
<UserProfile />
</Shimmer>
}
>
<UserProfile userId="123" />
</Suspense>
);
}Suspense Loading Behavior
React handles the fallback lifecycle differently from normal state transitions. The Suspense boundary renders the fallback when the child component suspends. Once the suspended component resolves, React replaces the entire fallback with the real component. The Shimmer component never receives a loading state change because React unmounts it completely.
This means you always set loading to true in Suspense fallbacks. The component never needs to transition from loading to loaded state.
Suspense Performance Optimization
Memoize your fallback component to prevent unnecessary re-renders during the Suspense lifecycle.
const ShimmerFallback = React.memo(() => (
<Shimmer loading={true} templateProps={{ user: userTemplate }}>
<UserProfile />
</Shimmer>
));
<Suspense fallback={<ShimmerFallback />}>
<UserProfile userId="123" />
</Suspense>Keep template data simple because the library measures the DOM synchronously through useLayoutEffect. Complex template logic creates unnecessary work during the measurement phase.
Global Configuration
Configure default shimmer settings across your application using the framework-specific configuration system. This sets consistent colors, animation timing, and border radius values without repeating props on every Shimmer component.
React Global Configuration
Wrap your application in the ShimmerProvider component and pass configuration values through the config prop.
import { Shimmer, ShimmerProvider } from '@shimmer-from-structure/react';
function App() {
return (
<ShimmerProvider
config={{
shimmerColor: 'rgba(56, 189, 248, 0.4)',
backgroundColor: 'rgba(56, 189, 248, 0.1)',
duration: 2.5,
fallbackBorderRadius: 8,
}}
>
<Dashboard />
</ShimmerProvider>
);
}Vue Global Configuration
Call the provideShimmerConfig function in your root component to inject configuration values into the component tree.
<script setup>
import { provideShimmerConfig } from '@shimmer-from-structure/vue';
provideShimmerConfig({
shimmerColor: 'rgba(56, 189, 248, 0.4)',
backgroundColor: 'rgba(56, 189, 248, 0.1)',
duration: 2.5,
fallbackBorderRadius: 8,
});
</script>
<template>
<router-view />
</template>Svelte Global Configuration
Use the setShimmerConfig function in any parent component to establish global configuration.
<script>
import { setShimmerConfig } from '@shimmer-from-structure/svelte';
setShimmerConfig({
shimmerColor: 'rgba(56, 189, 248, 0.4)',
backgroundColor: 'rgba(56, 189, 248, 0.1)',
duration: 2.5,
fallbackBorderRadius: 8,
});
</script>
<Dashboard />Configuration Inheritance and Overrides
Components inside the configured scope inherit these values automatically. You can override global settings on individual Shimmer components by passing props directly.
React example:
<Shimmer loading={true}><UserCard /></Shimmer>
<Shimmer loading={true} duration={0.5}><FastCard /></Shimmer>Vue example:
<Shimmer :loading="true"><UserCard /></Shimmer>
<Shimmer :loading="true" :duration="0.5"><FastCard /></Shimmer>Svelte example:
<Shimmer loading={true}><UserCard /></Shimmer>
<Shimmer loading={true} duration={0.5}><FastCard /></Shimmer>Accessing Configuration in Components
Access the current configuration values in your own components through framework-specific hooks and composables.
React hook:
import { useShimmerConfig } from 'shimmer-from-structure';
function MyComponent() {
const config = useShimmerConfig();
return <div style={{ background: config.backgroundColor }}>...</div>;
}Vue composable:
import { useShimmerConfig } from '@shimmer-from-structure/vue';
const config = useShimmerConfig();
console.log(config.value.backgroundColor);Svelte function:
import { getShimmerConfig } from '@shimmer-from-structure/svelte';
const config = getShimmerConfig();
console.log(config.backgroundColor);Related Resources
- React Skeleton: A skeleton screen component for React that works with CSS-in-JS styling solutions.
- React Content Loader: Build custom skeleton screens using SVG primitives with React components.
FAQs
Q: Can I use this library with Next.js App Router?
A: The library works with both Pages Router and App Router in Next.js. Import from shimmer-from-structure or @shimmer-from-structure/react and wrap your components as shown in the React examples. Server components need to delegate loading states to client components that use the Shimmer wrapper.
Q: How do I handle components that load data at different times?
A: Wrap each independently loading section in its own Shimmer component with separate loading state variables. This creates independent skeleton screens that resolve individually instead of waiting for all data to load.
Q: What happens if my template data structure does not match real data?
A: The shimmer layout will match the template structure rather than the real data structure. If your template has three list items but real data has ten items, the skeleton shows three blocks. Always match your template array lengths and object shapes to expected real data.
Q: Can I use custom colors that match my design system?
A: Pass shimmerColor and backgroundColor props to individual Shimmer components or set them globally through ShimmerProvider, provideShimmerConfig, or setShimmerConfig depending on your framework. The library accepts any valid CSS color string including rgba, hex, or hsl values.





