React Guide

Learn how to use Shimmer From Structure with React. The React adapter is built into the main package for backward compatibility.

Installation

npm install shimmer-from-structure

Or use the dedicated React package:

npm install @shimmer-from-structure/react

Basic Usage

Static Content

For components with hardcoded/static content:

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

function UserCard() {
  const [loading, setLoading] = useState(true);

  return (
    <Shimmer loading={loading}>
      <div className="card">
        <img src="avatar.jpg" className="avatar" />
        <h2>John Doe</h2>
        <p>Software Engineer</p>
      </div>
    </Shimmer>
  );
}

Dynamic Content with templateProps

For components that receive dynamic data via props, use templateProps:

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

// Your component that accepts props
const UserCard = ({ user }) => (
  <div className="card">
    <img src={user.avatar} className="avatar" />
    <h2>{user.name}</h2>
    <p>{user.role}</p>
  </div>
);

// 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 (
    <Shimmer loading={loading} templateProps={{ user: userTemplate }}>
      <UserCard user={user || userTemplate} />
    </Shimmer>
  );
}

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.

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>
  );
}

Performance Tips for Suspense

Memoize the fallback to prevent re-renders:

const ShimmerFallback = React.memo(() => (
  <Shimmer loading={true} templateProps={{ user: userTemplate }}>
    <UserProfile />
  </Shimmer>
));

// Usage
<Suspense fallback={<ShimmerFallback />}>
  <UserProfile userId="123" />
</Suspense>

Global Configuration

Set default configuration for your entire app using the Context API:

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>
  );
}

Components inside the provider automatically inherit these values:

// Inherits blue theme from provider
<Shimmer loading={true}><UserCard /></Shimmer>

// Overrides provider settings
<Shimmer loading={true} duration={0.5}><FastCard /></Shimmer>

Accessing Config in Hooks

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

function MyComponent() {
  const config = useShimmerConfig();
  return <div style={{ background: config.backgroundColor }}>...</div>;
}

Examples

Dashboard with Multiple Sections

import React, { useState, useEffect, Suspense, lazy } from 'react';
import { Shimmer, ShimmerProvider } from '@shimmer-from-structure/react';

// lazy-load components to demonstrate Suspense integration
const LazyNotifications = lazy(() => import('./Notifications'));

function Dashboard() {
  // Independent loading states for each section
  const [loadingUser, setLoadingUser] = useState(true);
  const [loadingStats, setLoadingStats] = useState(true);
  const [loadingTransactions, setLoadingTransactions] = useState(true);
  
  // Data states
  const [user, setUser] = useState(null);
  const [stats, setStats] = useState(null);
  const [transactions, setTransactions] = useState(null);

  useEffect(() => {
    // Simulate independent API calls
    setTimeout(() => { setUser(realUser); setLoadingUser(false); }, 800);
    setTimeout(() => { setStats(realStats); setLoadingStats(false); }, 1200);
    setTimeout(() => { setTransactions(realTransactions); setLoadingTransactions(false); }, 2000);
  }, []);

  return (
    <div className="dashboard">
      {/* User Profile Section */}
      <section className="dashboard-section">
        <Shimmer loading={loadingUser} templateProps={{ user: userTemplate }}>
          <UserProfile user={user || userTemplate} />
        </Shimmer>
      </section>

      {/* Stats Section with Custom Color */}
      <section className="dashboard-section">
        <Shimmer 
          loading={loadingStats} 
          templateProps={{ stats: statsTemplate }}
          shimmerColor="rgba(20, 184, 166, 0.2)"
        >
          <StatsGrid stats={stats || statsTemplate} />
        </Shimmer>
      </section>

      {/* Suspense Integration */}
      <section className="dashboard-section">
        <Suspense fallback={
          <Shimmer loading={true} templateProps={{ notifications: notificationsTemplate }}>
            <NotificationsList notifications={notificationsTemplate} />
          </Shimmer>
        }>
          <LazyNotifications />
        </Suspense>
      </section>

      {/* Transactions List */}
      <section className="dashboard-section">
        <Shimmer loading={loadingTransactions}>
          <TransactionsList transactions={transactions || transactionsTemplate} />
        </Shimmer>
      </section>
    </div>
  );
}

Next Steps