Infinite scroll

React Bootstrap 5 Infinite scroll

This feature adds a scroll event listener (to the window or the component it's attached to if it has the overflow-y property set to scroll) and calls a callback method every time a user reaches an end of a page/container.

Note: Read the API tab to find all available options and advanced customization


Basic example

Scroll down the container below to add more items.

Note: Your element should be scrollable, for example, it should have overflow-y: scroll property like in the example below.

  • Angry
  • Dizzy
  • Flushed
  • Frown
  • Grimace
  • Grin
import React, { useState, useRef } from 'react';
import { MDBInfiniteScroll } from 'mdb-react-ui-kit';

export default function App() {
  const infiniteRef = useRef<HTMLDivElement>(null);

  const [itemIndex, setItemIndex] = useState(0);
  const [renderedIcons, setRenderedIcons] = useState(['Angry', 'Dizzy', 'Flushed', 'Frown', 'Grimace', 'Grin']);

  const icons = ['Sad-Tear', 'Meh-Blank', 'Smile-Wink', 'Tired', 'Surprise', 'Kiss-Beam', 'Laugh-Squint'];

  const handleScroll = () => {
    if (itemIndex > icons.length - 1) {
      return;
    }

    setRenderedIcons([...renderedIcons, icons[itemIndex]]);

    setItemIndex(itemIndex + 1);
  };

  return (
    <MDBInfiniteScroll
      infiniteScrollRef={infiniteRef}
      className='container list-group'
      style={{ maxHeight: '261px', overflowY: 'scroll' }}
      onInfiniteScroll={handleScroll}
    >
      {renderedIcons.map((icon) => (
        <li key={icon.toLocaleLowerCase()} className='list-group-item d-flex align-items-center'>
          <i className={`far fa-${icon.toLowerCase()} fa-3x me-4`}></i>
          {icon}
        </li>
      ))}
    </MDBInfiniteScroll>
  );
}

Direction

Use infiniteDirection property to define the scrolling direction.

Angry Dizzy Flushed Grimace Grin
import React, { useState, useRef } from 'react';
import { MDBInfiniteScroll } from 'mdb-react-ui-kit';

export default function App() {
  const directionRef = useRef<HTMLDivElement>(null);

  const [directionIndex, setDirectionIndex] = useState(0);
  const [directionIcons, setDirectionIcons] = useState(['Angry', 'Dizzy', 'Flushed', 'Frown', 'Grimace', 'Grin']);

  const icons = ['Sad-Tear', 'Meh-Blank', 'Smile-Wink', 'Tired', 'Surprise', 'Kiss-Beam', 'Laugh-Squint'];

  const handleDirectionScroll = () => {
    if (directionIndex > icons.length - 1) {
      return;
    }

    setDirectionIcons([...directionIcons, icons[directionIndex]]);

    setDirectionIndex(directionIndex + 1);
  };

  return (
    <MDBInfiniteScroll
      infiniteDirection='x'
      infiniteScrollRef={directionRef}
      className='py-3 text-center'
      style={{ maxWidth: '1500px', overflowX: 'scroll', whiteSpace: 'nowrap' }}
      onInfiniteScroll={handleDirectionScroll}
    >
      {directionIcons.map((icon) => (
        <span key={icon.toLocaleLowerCase()} className='mx-5'>
          <i className={`far fa-${icon.toLocaleLowerCase()} fa-3x me-4`} /> {icon}
        </span>
      ))}
    </MDBInfiniteScroll>
  );
}

Spinners and asynchronous data

import React, { useState, useRef } from 'react';
import { MDBInfiniteScroll, MDBSpinner } from 'mdb-react-ui-kit';

export default function App() {
  const asyncRef = useRef<HTMLDivElement>(null);
  const [isSpinnerHidden, setIsSpinnerHidden] = useState(false);
  const [asyncIndex, setAsyncIndex] = useState(0);
  const [asyncImages, setAsyncImages] = useState([
    'https://mdbootstrap.com/img/Photos/Slides/img%20(100).webp',
    'https://mdbootstrap.com/img/Photos/Slides/img%20(105).webp',
    'https://mdbootstrap.com/img/Photos/Slides/img%20(106).webp',
  ]);

  const images = [
    'https://mdbootstrap.com/img/Photos/Slides/img%20(102).webp',
    'https://mdbootstrap.com/img/Photos/Slides/img%20(103).webp',
    'https://mdbootstrap.com/img/Photos/Slides/img%20(104).webp',
    'https://mdbootstrap.com/img/Photos/Slides/img%20(107).webp',
    'https://mdbootstrap.com/img/Photos/Slides/img%20(108).webp',
    'https://mdbootstrap.com/img/Photos/Slides/img%20(109).webp',
    'https://mdbootstrap.com/img/Photos/Slides/img%20(110).webp',
  ];


  const handleAsyncScroll = () => {
    if (asyncIndex > images.length - 1) {
      return;
    }

    setIsSpinnerHidden(false);

    setTimeout(() => {
      setAsyncImages([...asyncImages, images[asyncIndex]]);

      setIsSpinnerHidden(true);
      setAsyncIndex(asyncIndex + 1);
    }, 1000);
  };

  return (
    <MDBInfiniteScroll
      infiniteScrollRef={asyncRef}
      className='container list-group'
      style={{ maxHeight: '500px', overflowY: 'scroll' }}
      onInfiniteScroll={handleAsyncScroll}
    >
      <div>
        {asyncImages.map((image, index) => (
          <img src={image} key={index} className='img-fluid mb-3' />
        ))}
      </div>

      <div>
        <MDBSpinner
          tag='div'
          role='status'
          style={{ display: isSpinnerHidden ? 'none' : 'block' }}
          className='mx-auto'
        />
      </div>
    </MDBInfiniteScroll>
  );
}

Window

You can set a window as the parent of MDBInfiniteScroll component by using windowParent property.

import React, { useState, useRef } from 'react';
import { MDBContainer, MDBRow, MDBCol, MDBRipple, MDBBtn, MDBInfiniteScroll } from 'mdb-react-ui-kit';

export default function App() {
  const newImages = [
    {
      first: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/31.webp',
      second: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/23.webp',
    },
    {
      first: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/29.webp',
      second: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/27.webp',
    },
    {
      first: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/25.webp',
      second: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/24.webp',
    },
    {
      first: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/31.webp',
      second: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/32.webp',
    },
  ];

  const [imgCounter, setImgCounter] = useState(0);

  const [images, setImages] = useState([
    {
      first: 'https://mdbootstrap.com/img/Photos/Others/images/29.webp',
      second: 'https://mdbootstrap.com/img/Photos/Others/images/27.webp',
    },
    {
      first: 'https://mdbootstrap.com/img/Photos/Others/images/25.webp',
      second: 'https://mdbootstrap.com/img/Photos/Others/images/24.webp',
    },
    {
      first: 'https://mdbootstrap.com/img/Photos/Others/images/31.webp',
      second: 'https://mdbootstrap.com/img/Photos/Others/images/23.webp',
    },
  ]);

  const handleScroll = () => {
    if (imgCounter > newImages.length - 1) {
      return;
    }

    setImages([...images, newImages[imgCounter]]);
    setImgCounter(imgCounter + 1);
  };

  return (
    <main className='my-4'>
      <MDBContainer>
        <MDBInfiniteScroll windowParent onInfiniteScroll={handleScroll} tag='section' className='text-center mb-4'>
          {images.map((img, index) => (
            <MDBRow key={index}>
              <MDBCol md='6' className='mb-4'>
                <MDBRipple
                  rippleTag='div'
                  className='bg-image hover-overlay shadow-1-strong rounded mb-4'
                  rippleColor='light'
                >
                  <img src={img.first} className='w-100' />
                  <a href='#!'>
                    <div className='mask' style={{ backgroundColor: 'rgba(251, 251, 251, 0.2)' }}></div>
                  </a>
                </MDBRipple>
                <h5>This is an title of the article</h5>

                <p>
                  Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus
                  laudantium cum dolorem illo. Quos architecto deserunt saepe.
                </p>

                <MDBBtn rounded color='info' href='#!'>
                  Read more
                </MDBBtn>
              </MDBCol>

              <MDBCol md='6' className='mb-4'>
                <MDBRipple
                  rippleTag='div'
                  className='bg-image hover-overlay shadow-1-strong rounded mb-4'
                  rippleColor='light'
                >
                  <img src={img.second} className='w-100' />
                  <a href='#!'>
                    <div className='mask' style={{ backgroundColor: 'rgba(251, 251, 251, 0.2)' }}></div>
                  </a>
                </MDBRipple>
                <h5>This is an title of the article</h5>

                <p>
                  Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus
                  laudantium cum dolorem illo. Quos architecto deserunt saepe.
                </p>

                <MDBBtn rounded color='info' href='#!'>
                  Read more
                </MDBBtn>
              </MDBCol>
            </MDBRow>
          ))}

          <MDBRow id='spinner' style={{ display: 'none' }}>
            <div className='col-md-12'>
              <div className='spinner-border mx-auto'></div>
            </div>
          </MDBRow>
        </MDBInfiniteScroll>
      </MDBContainer>
    </main>
  );
}

Infinite scroll - API


Import

import { MDBInfiniteScroll } from 'mdb-react-ui-kit';

Properties

MDBInfiniteScroll

Name Type Default Description Example
infiniteScrollRef Reference '' A reference for the MDBInfiniteScroll element <MDBInfiniteScroll infiniteScrollRef={exampleReactRef} />
infiniteDirection String 'y' Defines an example scroll direction. <MDBInfiniteScroll infiniteDirection='x' />
tag String 'div' Defines tag of the MDBInfiniteScroll element <MDBInfiniteScroll tag="section" />
windowParent Boolean 'false' Sets window as a parent of MDBInfiniteScroll <MDBInfiniteScroll windowParent />

Events

MDBInfiniteScroll

Name Type Description
onInfiniteScroll () => any This event fires immediately after scrolling to the end of the container.
onComplete () => any This event fires immediately when the scrolling is completed.