Vue 3 adapter for shimmer-from-structure
npm install @shimmer-from-structure/vueA React, Vue, Svelte, Angular & SolidJS 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.
!React
!Vue
!Svelte
!Angular
!SolidJS
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:
- ✅ Works with React, Vue, Svelte, Angular & SolidJS - Simple, framework-specific adapters
- ✅ 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
``bash`
npm install shimmer-from-structureor
yarn add shimmer-from-structureor
pnpm add shimmer-from-structure
Shimmer From Structure provides dedicated packages for React and Vue.
React support is built into the main package for backward compatibility:
`javascript`
// React projects (or @shimmer-from-structure/react)
import { Shimmer } from 'shimmer-from-structure';
Vue support requires importing from the specific adapter:
`javascript`
// Vue 3 projects
import { Shimmer } from '@shimmer-from-structure/vue';
Svelte support is provided via its own adapter:
`javascript`
// Svelte projects
import { Shimmer } from '@shimmer-from-structure/svelte';
Angular support requires importing from the specific adapter:
`typescript`
// Angular projects
import { ShimmerComponent } from '@shimmer-from-structure/angular';
SolidJS support requires importing from the specific adapter:
`tsx`
// SolidJS projects
import { Shimmer } from '@shimmer-from-structure/solid';
---
For components with hardcoded/static content:
`tsx
import { Shimmer } from 'shimmer-from-structure';
function UserCard() { Software Engineer
return (
![]()
John Doe
);
}
`
`vue
Software Engineer
![]()
John Doe
`
`svelte
Software Engineer
![]()
John Doe
`
`typescript
import { Component, signal } from '@angular/core';
import { ShimmerComponent } from '@shimmer-from-structure/angular';
@Component({ Software Engineer
selector: 'app-user-card',
standalone: true,
imports: [ShimmerComponent],
template:
![]()
John Doe
,`
})
export class UserCardComponent {
isLoading = signal(true);
}
`tsx
import { createSignal } from 'solid-js';
import { Shimmer } from '@shimmer-from-structure/solid';
function UserCard() {
const [isLoading, setIsLoading] = createSignal(true);
return ( Software Engineer
![]()
John Doe
);
}
`
---
For components that receive dynamic data via props, use templateProps to provide mock data for skeleton generation:
React
`tsx
import { Shimmer } from 'shimmer-from-structure';
// Your component that accepts props
const UserCard = ({ user }) => (
{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 (
);
}
`
Vue
`vue
`
Svelte
`svelte
`
Angular
`typescript
import { Component, signal } from '@angular/core';
import { ShimmerComponent } from '@shimmer-from-structure/angular';
import { UserCardComponent } from './user-card.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [ShimmerComponent, UserCardComponent],
template:
,
})
export class AppComponent {
loading = signal(true);
user = signal
userTemplate = {
name: 'Loading...',
role: 'Loading role...',
avatar: 'placeholder.jpg',
};
}
`
SolidJS
`tsx
import { createSignal } from 'solid-js';
import { Shimmer } from '@shimmer-from-structure/solid';
import { UserCard } from './UserCard';
function App() {
const [loading, setLoading] = createSignal(true);
const [user, setUser] = createSignal(null);
const userTemplate = {
name: 'Loading...',
role: 'Loading role...',
avatar: 'placeholder.jpg',
};
return (
);
}
`
The templateProps object is spread onto the first child component when loading, allowing it to render with mock data for measurement.
| 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 |
React
`tsx`
shimmerColor="rgba(255, 255, 255, 0.2)"
backgroundColor="rgba(255, 255, 255, 0.1)"
duration={2}
fallbackBorderRadius={8}
templateProps={{
user: userTemplate,
settings: settingsTemplate,
}}
>
Vue
`vue`
shimmerColor="rgba(255, 255, 255, 0.2)"
backgroundColor="rgba(255, 255, 255, 0.1)"
:duration="2"
:fallbackBorderRadius="8"
:templateProps="{
user: userTemplate,
settings: settingsTemplate,
}"
>
Svelte
`svelte`
shimmerColor="rgba(255, 255, 255, 0.2)"
backgroundColor="rgba(255, 255, 255, 0.1)"
duration={2}
fallbackBorderRadius={8}
templateProps={{
user: userTemplate,
settings: settingsTemplate,
}}
>
Angular
`typescript`
shimmerColor="rgba(255, 255, 255, 0.2)"
backgroundColor="rgba(255, 255, 255, 0.1)"
[duration]="2"
[fallbackBorderRadius]="8"
[templateProps]="{
user: userTemplate,
settings: settingsTemplate
}">
[settings]="settings()" />
SolidJS
`tsx`
shimmerColor="rgba(255, 255, 255, 0.2)"
backgroundColor="rgba(255, 255, 255, 0.1)"
duration={2}
fallbackBorderRadius={8}
templateProps={{
user: userTemplate,
settings: settingsTemplate,
}}
>
1. Visible Container Rendering: When loading={true}, your component renders with transparent text but visible container backgroundstemplateProps
2. Template Props Injection: If is provided, it's spread onto the first child so dynamic components can renderuseLayoutEffect
3. DOM Measurement: Uses to synchronously measure all leaf elements via getBoundingClientRect()border-radius
4. Border Radius Detection: Automatically captures each element's computed from CSS
5. Shimmer Generation: Creates absolutely-positioned shimmer blocks matching measured dimensions
6. Animation: Applies smooth gradient animation that sweeps across each block
- Container backgrounds visible: Unlike opacity: 0, we use color: transparent so card backgrounds/borders show during loadingborder-radius: 0
- Auto border-radius: Circular avatars get circular shimmer blocks automatically
- Fallback radius: Text elements (which have ) use fallbackBorderRadius to avoid sharp rectangles
- Dark-mode friendly: Default colors use semi-transparent whites that work on any background
Each section can have its own independent loading state:
React
`tsx
function Dashboard() {
const [loadingUser, setLoadingUser] = useState(true);
const [loadingStats, setLoadingStats] = useState(true);
return (
<>
{/ User profile section /}
{/ Stats section - with custom colors /}
templateProps={{ stats: statsTemplate }}
shimmerColor="rgba(20, 184, 166, 0.2)"
>
>
);
}
`
Vue
`vue
:templateProps="{ stats: statsTemplate }"
shimmerColor="rgba(20, 184, 166, 0.2)"
>
`
Svelte
`svelte
templateProps={{ stats: statsTemplate }}
shimmerColor="rgba(20, 184, 166, 0.2)"
>
`
Angular
`typescript
@Component({
template:
[templateProps]="{ stats: statsTemplate }"
shimmerColor="rgba(20, 184, 166, 0.2)"
>
,`
})
export class DashboardComponent {
loadingUser = signal(true);
loadingStats = signal(true);
// ...
}
React
`tsx`
Vue
`vue`
Svelte
`svelte`
Angular
`typescript`
[templateProps]="{ transactions: transactionsTemplate }">
React
`tsx`
Vue
`vue`
Svelte
`svelte`
Angular
`typescript`
[templateProps]="{ members: teamTemplate }">
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.
`tsx
import { Suspense, lazy } from 'react';
import { Shimmer } from 'shimmer-from-structure';
const UserProfile = lazy(() => import('./UserProfile'));
function App() {
return (
}
>
);
}
`
When using Shimmer as a Suspense fallback:
1. Suspend: React renders the fallback → Shimmer shows with loading={true}loading
2. Resolve: React replaces the entire fallback with the real component
3. The Shimmer is unmounted, not updated — so you never need to toggle
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.
You can set default configuration for your entire app (or specific sections) using the context/provider pattern. This is perfect for maintaining consistent themes without repeating props.
`tsx
import { Shimmer, ShimmerProvider } from '@shimmer-from-structure/react';
function App() {
return (
// Set global defaults
shimmerColor: 'rgba(56, 189, 248, 0.4)', // Blue shimmer
backgroundColor: 'rgba(56, 189, 248, 0.1)', // Blue background
duration: 2.5,
fallbackBorderRadius: 8,
}}
>
);
}
`
`vue
`
`svelte
`
`typescript
// main.ts or bootstrapApplication
import { bootstrapApplication } from '@angular/platform-browser';
import { provideShimmerConfig } from '@shimmer-from-structure/angular';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, {
providers: [
provideShimmerConfig({
shimmerColor: 'rgba(56, 189, 248, 0.4)',
backgroundColor: 'rgba(56, 189, 248, 0.1)',
duration: 2.5,
fallbackBorderRadius: 8,
}),
],
});
`
`tsx
import { Shimmer, ShimmerProvider } from '@shimmer-from-structure/solid';
function App() {
return (
shimmerColor: 'rgba(56, 189, 248, 0.4)',
backgroundColor: 'rgba(56, 189, 248, 0.1)',
duration: 2.5,
fallbackBorderRadius: 8,
}}
>
);
}
`
---
Components inside the provider automatically inherit values. You can still override them locally:
React
`tsx
// Inherits blue theme from provider
// Overrides provider settings
`
Vue
`vue
`
Svelte
`svelte
`
Angular
`typescript
`
SolidJS
`tsx
`
If you need to access the current configuration in your own components:
React
`tsx
import { useShimmerConfig } from 'shimmer-from-structure';
function MyComponent() {
const config = useShimmerConfig();
return
Vue
`javascript
import { useShimmerConfig } from '@shimmer-from-structure/vue';const config = useShimmerConfig();
console.log(config.value.backgroundColor);
`Svelte
`javascript
import { getShimmerConfig } from '@shimmer-from-structure/svelte';const config = getShimmerConfig();
console.log(config.backgroundColor);
`Angular
`typescript
import { Component, inject } from '@angular/core';
import { injectShimmerConfig } from '@shimmer-from-structure/angular';@Component({
selector: 'app-my-component',
template:
,
})
export class MyComponent {
config = injectShimmerConfig();
}
`SolidJS
`tsx
import { useShimmerConfig } from '@shimmer-from-structure/solid';function MyComponent() {
const config = useShimmerConfig();
return
...;
}
`Best Practices
$3
When your component receives data via props, always provide
templateProps with mock data that matches the expected structure.$3
Ensure your template data has the same array length and property structure as real data for accurate shimmer layout.
$3
Wrap each section in its own Shimmer for independent loading states:
`tsx
// ✅ Good - independent loading
// ❌ Avoid - all-or-nothing loading
`$3
Block elements like
, take full container width. If you want shimmer to match text width:
`css`
.title {
width: fit-content;
}
For async components (like charts), ensure containers have explicit dimensions so shimmer has something to measure.
- Measurement happens only when loading changes to trueuseLayoutEffect
- Uses for synchronous measurement (no flicker)
- Minimal re-renders - only updates when loading state or children change
- Lightweight DOM measurements using native browser APIs
- Lightweight DOM measurements using native browser APIs
This is a monorepo managed with npm workspaces. Each package can be built independently:
`bashInstall dependencies
npm install
📝 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