DEV Community

Joseph Nelson
Joseph Nelson

Posted on

Getting Started with React Spinners CSS: Building Loading Indicators

React Spinners CSS is a lightweight library for creating CSS-animated loading spinners in React applications. It provides a collection of pure CSS spinners that are easy to use and customize, perfect for indicating loading states without JavaScript animations. This guide walks through setting up and creating loading spinners using React Spinners CSS with React, from installation to a working implementation. This is part 52 of a series on using React Spinners CSS with React.
https://bit.cloud/joshk/react-spinners-css

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 components
  • Familiarity with JavaScript/TypeScript
  • Understanding of CSS

Installation

Install React Spinners CSS using your preferred package manager:

npm install react-spinners-css
Enter fullscreen mode Exit fullscreen mode

Or with yarn:

yarn add react-spinners-css
Enter fullscreen mode Exit fullscreen mode

Or with pnpm:

pnpm add react-spinners-css
Enter fullscreen mode Exit fullscreen mode

After installation, your package.json should include:

{
  "dependencies": {
    "react-spinners-css": "^1.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Project Setup

React Spinners CSS requires minimal setup. Import the spinner components and you're ready to use them.

First Example / Basic Usage

Let's create a simple loading spinner. Create a new file src/SpinnerExample.jsx:

// src/SpinnerExample.jsx
import React, { useState } from 'react';
import { Spinner } from 'react-spinners-css';

function SpinnerExample() {
  const [isLoading, setIsLoading] = useState(false);

  const handleLoad = async () => {
    setIsLoading(true);
    // Simulate API call
    await new Promise(resolve => setTimeout(resolve, 2000));
    setIsLoading(false);
  };

  return (
    <div style={{ padding: '20px', textAlign: 'center' }}>
      <h2>Loading Spinner Example</h2>
      <button
        onClick={handleLoad}
        disabled={isLoading}
        style={{
          padding: '10px 20px',
          backgroundColor: '#007bff',
          color: 'white',
          border: 'none',
          borderRadius: '4px',
          cursor: isLoading ? 'not-allowed' : 'pointer',
          marginBottom: '20px'
        }}
      >
        {isLoading ? 'Loading...' : 'Start Loading'}
      </button>
      {isLoading && (
        <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
          <Spinner color="#007bff" />
        </div>
      )}
    </div>
  );
}

export default SpinnerExample;
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

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

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

export default App;
Enter fullscreen mode Exit fullscreen mode

This creates a loading spinner that appears when the button is clicked and disappears after the async operation completes.

Understanding the Basics

React Spinners CSS provides several spinner types:

  • Spinner: Basic spinner
  • Circle: Circle spinner
  • Bounce: Bouncing dots
  • Pulse: Pulsing animation
  • Wave: Wave animation
  • Rotate: Rotating spinner
  • And more CSS-based spinners

Key concepts:

  • CSS Animations: All spinners use pure CSS animations for performance
  • Simple API: Easy to use with minimal configuration
  • Customization: Color and size can be customized
  • Lightweight: No JavaScript animations, just CSS
  • Conditional Rendering: Show/hide spinners based on loading state

Here's an example with different spinner types:

// src/MultipleSpinnersExample.jsx
import React, { useState } from 'react';
import { Spinner, Circle, Bounce, Pulse, Wave } from 'react-spinners-css';

function MultipleSpinnersExample() {
  const [selectedSpinner, setSelectedSpinner] = useState('Spinner');

  const spinners = {
    Spinner: <Spinner color="#007bff" />,
    Circle: <Circle color="#28a745" />,
    Bounce: <Bounce color="#ffc107" />,
    Pulse: <Pulse color="#dc3545" />,
    Wave: <Wave color="#6c757d" />
  };

  return (
    <div style={{ padding: '20px', textAlign: 'center' }}>
      <h2>Multiple Spinner Types</h2>
      <div style={{ marginBottom: '20px' }}>
        <label>Select Spinner: </label>
        <select
          value={selectedSpinner}
          onChange={(e) => setSelectedSpinner(e.target.value)}
          style={{ padding: '5px', marginLeft: '10px' }}
        >
          {Object.keys(spinners).map(type => (
            <option key={type} value={type}>{type}</option>
          ))}
        </select>
      </div>
      <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '200px' }}>
        {spinners[selectedSpinner]}
      </div>
    </div>
  );
}

export default MultipleSpinnersExample;
Enter fullscreen mode Exit fullscreen mode

Practical Example / Building Something Real

Let's build a comprehensive loading system with different spinner types and use cases:

// src/LoadingSystem.jsx
import React, { useState } from 'react';
import { Spinner, Circle, Bounce, Pulse } from 'react-spinners-css';

function LoadingSystem() {
  const [loadingStates, setLoadingStates] = useState({
    button: false,
    inline: false,
    page: false,
    overlay: false
  });

  const simulateApiCall = async (type) => {
    setLoadingStates(prev => ({ ...prev, [type]: true }));
    await new Promise(resolve => setTimeout(resolve, 2000));
    setLoadingStates(prev => ({ ...prev, [type]: false }));
  };

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

      {/* Button Loading */}
      <section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
        <h2>Button Loading</h2>
        <button
          onClick={() => simulateApiCall('button')}
          disabled={loadingStates.button}
          style={{
            padding: '10px 20px',
            backgroundColor: '#007bff',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: loadingStates.button ? 'not-allowed' : 'pointer',
            display: 'flex',
            alignItems: 'center',
            gap: '10px'
          }}
        >
          {loadingStates.button && <Spinner color="white" size={20} />}
          {loadingStates.button ? 'Loading...' : 'Submit'}
        </button>
      </section>

      {/* Inline Loading */}
      <section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
        <h2>Inline Loading</h2>
        <button
          onClick={() => simulateApiCall('inline')}
          style={{
            padding: '10px 20px',
            backgroundColor: '#28a745',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer',
            marginBottom: '20px'
          }}
        >
          Load Data
        </button>
        {loadingStates.inline ? (
          <div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
            <Circle color="#007bff" />
          </div>
        ) : (
          <div style={{ padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '4px' }}>
            <p>Data loaded successfully!</p>
          </div>
        )}
      </section>

      {/* Page Loading */}
      <section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
        <h2>Page Loading</h2>
        <button
          onClick={() => simulateApiCall('page')}
          style={{
            padding: '10px 20px',
            backgroundColor: '#ffc107',
            color: 'black',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Load Page Content
        </button>
        {loadingStates.page && (
          <div style={{
            position: 'fixed',
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            backgroundColor: 'rgba(255, 255, 255, 0.9)',
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            zIndex: 1000
          }}>
            <div style={{ textAlign: 'center' }}>
              <Pulse color="#007bff" size={60} />
              <p style={{ marginTop: '20px', fontSize: '18px' }}>Loading page content...</p>
            </div>
          </div>
        )}
      </section>

      {/* Overlay Loading */}
      <section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px', position: 'relative' }}>
        <h2>Overlay Loading</h2>
        <button
          onClick={() => simulateApiCall('overlay')}
          style={{
            padding: '10px 20px',
            backgroundColor: '#dc3545',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Process Data
        </button>
        <div style={{
          padding: '20px',
          backgroundColor: '#f8f9fa',
          borderRadius: '4px',
          marginTop: '20px',
          minHeight: '200px'
        }}>
          <p>Content area that can be overlaid with a loading spinner.</p>
        </div>
        {loadingStates.overlay && (
          <div style={{
            position: 'absolute',
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            backgroundColor: 'rgba(0, 0, 0, 0.5)',
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            borderRadius: '8px'
          }}>
            <Bounce color="#ffffff" />
          </div>
        )}
      </section>
    </div>
  );
}

export default LoadingSystem;
Enter fullscreen mode Exit fullscreen mode

Now create a reusable loading component:

// src/components/LoadingIndicator.jsx
import React from 'react';
import { Spinner } from 'react-spinners-css';

function LoadingIndicator({ size = 50, color = '#007bff', fullScreen = false }) {
  if (fullScreen) {
    return (
      <div style={{
        position: 'fixed',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        backgroundColor: 'rgba(255, 255, 255, 0.9)',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        zIndex: 1000
      }}>
        <Spinner color={color} size={size} />
      </div>
    );
  }

  return (
    <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', padding: '20px' }}>
      <Spinner color={color} size={size} />
    </div>
  );
}

export default LoadingIndicator;
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

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

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

export default App;
Enter fullscreen mode Exit fullscreen mode

This example demonstrates:

  • Multiple spinner types
  • Button loading states
  • Inline loading indicators
  • Page-level loading
  • Overlay loading
  • Reusable loading component
  • Custom sizing and colors

Common Issues / Troubleshooting

  1. Spinner not displaying: Make sure you're importing the correct spinner component. Each spinner type is a separate component that needs to be imported individually.

  2. Spinner too small or large: Use the size prop to control the spinner size. Not all spinners support the size prop, so check the documentation for your specific spinner type.

  3. Color not changing: Use the color prop to set the spinner color. It accepts any valid CSS color value (hex, rgb, named colors).

  4. Spinner not animating: React Spinners CSS uses pure CSS animations. If the spinner isn't animating, check for CSS conflicts or ensure the component is properly rendered.

  5. Multiple spinners: You can use multiple spinner instances on the same page. Each spinner manages its own state independently.

  6. Performance: Since spinners use CSS animations, they're very performant. However, if you have many spinners, consider using conditional rendering to only show them when needed.

Next Steps

Now that you have a basic understanding of React Spinners CSS:

  • Explore all available spinner types
  • Learn about advanced customization options
  • Implement loading states in forms and data fetching
  • Add loading indicators to async operations
  • Create custom loading components
  • Learn about other loading libraries (react-loader-spinner, react-spinners)
  • Check the official repository: https://github.com/JoshK2/react-spinners-css
  • Look for part 53 of this series for more advanced topics

Summary

You've successfully set up React Spinners CSS in your React application and created loading indicators for buttons, inline content, page loading, and overlays. React Spinners CSS provides lightweight, CSS-animated spinners that are easy to use and customize.

SEO Keywords

react-spinners-css
React CSS spinner
react-spinners-css tutorial
React loading indicator
react-spinners-css installation
React CSS animation spinner
react-spinners-css example
React pure CSS spinner
react-spinners-css setup
React lightweight spinner
react-spinners-css customization
React spinner component
react-spinners-css types
React loading library
react-spinners-css getting started

Top comments (0)