DEV Community

Joseph Nelson
Joseph Nelson

Posted on

Advanced Skeleton Loaders with react-content-loader in React

react-content-loader is a SVG-powered library for creating placeholder loading animations (skeleton loaders) in React applications. It provides a flexible way to create Facebook-style loading placeholders that match your content structure, improving perceived performance and user experience. This guide walks through advanced usage of react-content-loader with React, including custom loaders, pre-defined patterns, and complex skeleton structures. This is part 54 of a series on using react-content-loader with React.

Prerequisites

Before you begin, make sure you have:

  • Node.js version 14.0 or higher installed
  • npm, yarn, or pnpm package manager
  • A React project (version 16.8 or higher) or create-react-app setup
  • Basic knowledge of React hooks (useState, useEffect)
  • Familiarity with SVG and CSS
  • Understanding of async operations and loading states

Installation

Install react-content-loader using your preferred package manager:

npm install react-content-loader
Enter fullscreen mode Exit fullscreen mode

Or with yarn:

yarn add react-content-loader
Enter fullscreen mode Exit fullscreen mode

Or with pnpm:

pnpm add react-content-loader
Enter fullscreen mode Exit fullscreen mode

After installation, your package.json should include:

{
  "dependencies": {
    "react-content-loader": "^7.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Project Setup

react-content-loader requires no additional setup. Import the components and you're ready to use them.

First Example / Basic Usage

Let's create a simple skeleton loader. Create a new file src/ContentLoaderExample.jsx:

// src/ContentLoaderExample.jsx
import React, { useState, useEffect } from 'react';
import ContentLoader from 'react-content-loader';

function ContentLoaderExample() {
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState(null);

  useEffect(() => {
    // Simulate API call
    setTimeout(() => {
      setData({ title: 'Loaded Content', description: 'This content was loaded successfully!' });
      setIsLoading(false);
    }, 2000);
  }, []);

  if (isLoading) {
    return (
      <div style={{ padding: '20px' }}>
        <ContentLoader
          viewBox="0 0 400 160"
          height={160}
          width={400}
          backgroundColor="#f3f3f3"
          foregroundColor="#ecebeb"
        >
          <rect x="0" y="0" rx="5" ry="5" width="400" height="160" />
        </ContentLoader>
      </div>
    );
  }

  return (
    <div style={{ padding: '20px' }}>
      <h2>{data.title}</h2>
      <p>{data.description}</p>
    </div>
  );
}

export default ContentLoaderExample;
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

// src/App.jsx
import React from 'react';
import ContentLoaderExample from './ContentLoaderExample';
import './App.css';

function App() {
  return (
    <div className="App">
      <ContentLoaderExample />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This creates a basic skeleton loader that displays while content is loading.

Understanding the Basics

react-content-loader provides several key features:

  • ContentLoader component: Main component for creating custom loaders
  • Pre-defined loaders: Facebook, Instagram, Code, List, BulletList patterns
  • SVG-based: Uses SVG for smooth animations
  • Customizable: Colors, speed, size, and shapes
  • Responsive: Works with different screen sizes

Key concepts for advanced usage:

  • SVG Shapes: Use <rect>, <circle>, and other SVG elements to create loaders
  • viewBox: Defines the coordinate system and aspect ratio
  • Animation: Automatic shimmer animation effect
  • Customization: Control colors, speed, and animation behavior
  • Pre-defined Patterns: Use ready-made loaders for common layouts

Here's an example with pre-defined loaders:

// src/PredefinedLoadersExample.jsx
import React, { useState } from 'react';
import { Facebook, Instagram, Code, List, BulletList } from 'react-content-loader';

function PredefinedLoadersExample() {
  const [selectedLoader, setSelectedLoader] = useState('Facebook');

  const loaders = {
    Facebook: <Facebook />,
    Instagram: <Instagram />,
    Code: <Code />,
    List: <List />,
    BulletList: <BulletList />
  };

  return (
    <div style={{ padding: '20px' }}>
      <h2>Pre-defined Loaders</h2>
      <div style={{ marginBottom: '20px' }}>
        <label>Select Loader: </label>
        <select
          value={selectedLoader}
          onChange={(e) => setSelectedLoader(e.target.value)}
          style={{ padding: '5px', marginLeft: '10px' }}
        >
          {Object.keys(loaders).map(type => (
            <option key={type} value={type}>{type}</option>
          ))}
        </select>
      </div>
      <div style={{ border: '1px solid #ddd', padding: '20px', borderRadius: '8px' }}>
        {loaders[selectedLoader]}
      </div>
    </div>
  );
}

export default PredefinedLoadersExample;
Enter fullscreen mode Exit fullscreen mode

Practical Example / Building Something Real

Let's build a comprehensive skeleton loader system with custom patterns:

// src/CardSkeletonLoader.jsx
import React from 'react';
import ContentLoader from 'react-content-loader';

function CardSkeletonLoader() {
  return (
    <ContentLoader
      viewBox="0 0 400 200"
      height={200}
      width={400}
      backgroundColor="#f3f3f3"
      foregroundColor="#ecebeb"
    >
      {/* Avatar */}
      <circle cx="50" cy="50" r="30" />
      {/* Title */}
      <rect x="100" y="20" rx="4" ry="4" width="200" height="20" />
      {/* Subtitle */}
      <rect x="100" y="50" rx="3" ry="3" width="150" height="15" />
      {/* Content lines */}
      <rect x="20" y="100" rx="3" ry="3" width="360" height="10" />
      <rect x="20" y="120" rx="3" ry="3" width="320" height="10" />
      <rect x="20" y="140" rx="3" ry="3" width="280" height="10" />
      {/* Button */}
      <rect x="20" y="170" rx="5" ry="5" width="100" height="25" />
    </ContentLoader>
  );
}

export default CardSkeletonLoader;
Enter fullscreen mode Exit fullscreen mode

Create a list skeleton loader:

// src/ListSkeletonLoader.jsx
import React from 'react';
import ContentLoader from 'react-content-loader';

function ListSkeletonLoader({ items = 3 }) {
  return (
    <div>
      {Array.from({ length: items }).map((_, index) => (
        <ContentLoader
          key={index}
          viewBox="0 0 400 80"
          height={80}
          width={400}
          backgroundColor="#f3f3f3"
          foregroundColor="#ecebeb"
          style={{ marginBottom: '10px' }}
        >
          {/* Icon */}
          <rect x="10" y="10" rx="5" ry="5" width="60" height="60" />
          {/* Title */}
          <rect x="80" y="20" rx="4" ry="4" width="200" height="15" />
          {/* Description */}
          <rect x="80" y="45" rx="3" ry="3" width="300" height="10" />
        </ContentLoader>
      ))}
    </div>
  );
}

export default ListSkeletonLoader;
Enter fullscreen mode Exit fullscreen mode

Create a table skeleton loader:

// src/TableSkeletonLoader.jsx
import React from 'react';
import ContentLoader from 'react-content-loader';

function TableSkeletonLoader({ rows = 5, columns = 4 }) {
  const cellWidth = 380 / columns;
  const cellHeight = 30;
  const rowHeight = 50;

  return (
    <ContentLoader
      viewBox={`0 0 400 ${rows * rowHeight + 40}`}
      height={rows * rowHeight + 40}
      width={400}
      backgroundColor="#f3f3f3"
      foregroundColor="#ecebeb"
    >
      {/* Header row */}
      {Array.from({ length: columns }).map((_, colIndex) => (
        <rect
          key={`header-${colIndex}`}
          x={10 + colIndex * cellWidth}
          y={10}
          rx="3"
          ry="3"
          width={cellWidth - 20}
          height={cellHeight}
        />
      ))}
      {/* Data rows */}
      {Array.from({ length: rows }).map((_, rowIndex) =>
        Array.from({ length: columns }).map((_, colIndex) => (
          <rect
            key={`row-${rowIndex}-col-${colIndex}`}
            x={10 + colIndex * cellWidth}
            y={50 + rowIndex * rowHeight}
            rx="3"
            ry="3"
            width={cellWidth - 20}
            height={cellHeight}
          />
        ))
      )}
    </ContentLoader>
  );
}

export default TableSkeletonLoader;
Enter fullscreen mode Exit fullscreen mode

Now create a complete loading system:

// src/AdvancedLoadingSystem.jsx
import React, { useState, useEffect } from 'react';
import CardSkeletonLoader from './CardSkeletonLoader';
import ListSkeletonLoader from './ListSkeletonLoader';
import TableSkeletonLoader from './TableSkeletonLoader';

function AdvancedLoadingSystem() {
  const [loadingStates, setLoadingStates] = useState({
    card: true,
    list: true,
    table: true
  });

  const [data, setData] = useState({
    card: null,
    list: null,
    table: null
  });

  useEffect(() => {
    // Simulate loading card data
    setTimeout(() => {
      setData(prev => ({
        ...prev,
        card: { title: 'Card Title', description: 'Card description' }
      }));
      setLoadingStates(prev => ({ ...prev, card: false }));
    }, 2000);

    // Simulate loading list data
    setTimeout(() => {
      setData(prev => ({
        ...prev,
        list: [
          { id: 1, title: 'Item 1', description: 'Description 1' },
          { id: 2, title: 'Item 2', description: 'Description 2' },
          { id: 3, title: 'Item 3', description: 'Description 3' }
        ]
      }));
      setLoadingStates(prev => ({ ...prev, list: false }));
    }, 2500);

    // Simulate loading table data
    setTimeout(() => {
      setData(prev => ({
        ...prev,
        table: [
          { id: 1, name: 'John', email: 'john@example.com' },
          { id: 2, name: 'Jane', email: 'jane@example.com' },
          { id: 3, name: 'Bob', email: 'bob@example.com' }
        ]
      }));
      setLoadingStates(prev => ({ ...prev, table: false }));
    }, 3000);
  }, []);

  return (
    <div style={{ padding: '20px' }}>
      <h1>Advanced Loading System</h1>

      {/* Card Section */}
      <section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
        <h2>Card Loading</h2>
        {loadingStates.card ? (
          <CardSkeletonLoader />
        ) : (
          <div style={{ padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '8px' }}>
            <h3>{data.card?.title}</h3>
            <p>{data.card?.description}</p>
          </div>
        )}
      </section>

      {/* List Section */}
      <section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
        <h2>List Loading</h2>
        {loadingStates.list ? (
          <ListSkeletonLoader items={3} />
        ) : (
          <div>
            {data.list?.map(item => (
              <div key={item.id} style={{ padding: '15px', marginBottom: '10px', backgroundColor: '#f8f9fa', borderRadius: '4px' }}>
                <h4>{item.title}</h4>
                <p>{item.description}</p>
              </div>
            ))}
          </div>
        )}
      </section>

      {/* Table Section */}
      <section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
        <h2>Table Loading</h2>
        {loadingStates.table ? (
          <TableSkeletonLoader rows={3} columns={3} />
        ) : (
          <table style={{ width: '100%', borderCollapse: 'collapse' }}>
            <thead>
              <tr style={{ backgroundColor: '#007bff', color: 'white' }}>
                <th style={{ padding: '10px', textAlign: 'left' }}>ID</th>
                <th style={{ padding: '10px', textAlign: 'left' }}>Name</th>
                <th style={{ padding: '10px', textAlign: 'left' }}>Email</th>
              </tr>
            </thead>
            <tbody>
              {data.table?.map(row => (
                <tr key={row.id} style={{ borderBottom: '1px solid #ddd' }}>
                  <td style={{ padding: '10px' }}>{row.id}</td>
                  <td style={{ padding: '10px' }}>{row.name}</td>
                  <td style={{ padding: '10px' }}>{row.email}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </section>
    </div>
  );
}

export default AdvancedLoadingSystem;
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

// src/App.jsx
import React from 'react';
import AdvancedLoadingSystem from './AdvancedLoadingSystem';
import PredefinedLoadersExample from './PredefinedLoadersExample';
import './App.css';

function App() {
  return (
    <div className="App">
      <AdvancedLoadingSystem />
      <PredefinedLoadersExample />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This example demonstrates:

  • Custom skeleton loaders for cards, lists, and tables
  • Pre-defined loader patterns
  • Multiple loading states
  • Responsive skeleton structures
  • Matching content layout

Common Issues / Troubleshooting

  1. Loader not displaying: Make sure you're using SVG elements (<rect>, <circle>) inside ContentLoader. Regular HTML elements won't work.

  2. Sizing issues: Use the viewBox prop to define the coordinate system. The width and height props control the rendered size.

  3. Animation not working: Check that animate prop is true (default). Also ensure you're using valid SVG shapes.

  4. Colors not applying: Use backgroundColor and foregroundColor props to customize colors. These control the shimmer animation colors.

  5. Layout mismatch: Design your skeleton loader to match your actual content layout. Use the same spacing and dimensions for best results.

  6. Performance: react-content-loader uses SVG which is performant, but if you have many loaders, consider using conditional rendering to only show them when needed.

Next Steps

Now that you have an advanced understanding of react-content-loader:

  • Explore all pre-defined loader patterns
  • Learn about advanced SVG customization
  • Create loaders that match your exact content structure
  • Implement responsive skeleton loaders
  • Add skeleton loaders to data fetching hooks
  • Learn about other skeleton loader libraries
  • Check the official repository: https://github.com/danilowoz/react-content-loader
  • Look for part 55 of this series for more advanced topics

Summary

You've successfully integrated react-content-loader into your React application with advanced features including custom skeleton loaders for cards, lists, and tables, pre-defined patterns, and comprehensive loading states. react-content-loader provides a flexible, SVG-based solution for creating professional skeleton loaders that improve perceived performance.

SEO Keywords

react-content-loader
React skeleton loader
react-content-loader tutorial
React placeholder loading
react-content-loader installation
React SVG skeleton
react-content-loader example
React loading placeholder
react-content-loader setup
React Facebook loader
react-content-loader customization
React skeleton pattern
react-content-loader SVG
React loading library
react-content-loader getting started

Top comments (0)