Universal Skeleton Generator for React, Vue, and Svelte – Shimmer From Structure

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-structure
yarn add shimmer-from-structure
pnpm add shimmer-from-structure

React 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.

Add Comment