DEV Community

Cover image for Customizing Swiper.js prev/next arrow buttons and pagination bullets in React
Ivad Yves HABIMANA
Ivad Yves HABIMANA

Posted on • Edited on

Customizing Swiper.js prev/next arrow buttons and pagination bullets in React

I was working on an image carousel using the Swiper.js library in a React project and I had a hard time customizing the navigation buttons and pagination bullets to match my design,
luckily after some long research, I was able to find a solution which I am going to share with you in today's article.

In this guide, we will set up Swiper in React using the Swiper Elements (WebComponents) and after getting Swiper to work, we will customize the navigation arrows and pagination bullets using custom CSS.

Prerequisites

  • A Working React project: to keep things short I will start with a React project already set therefore to follow along you will need to have a React project already set up.

I will be using Vite to set up the React project using this guide but you can also use create-react-app or any other way you want.

You can find the React setup I will be using in HERE

  • A code editor: I will be using Visual Studio Code but feel free to use any editor of your preference.

  • Since this is a React guide I am assuming prior basic experience working with JavaScript and React applications.

That's all you need Let's get started

1. Installing Swiper

Swiper JS is a library used to create sliders for web and mobile devices and it can integrate well with Vanilla JavaScript, React, Vue, and Web components

You can install Swiper by running the following command in the terminal



# For npm
npm install swiper
# For yarn
yarn add swiper
# For pnpm
pnpm install swiper



Enter fullscreen mode Exit fullscreen mode

This will install the Swiper package which will allow us to write the swipe logic.

Configuring Swiper in React

In many tutorials and articles, you will find Swipe being used in React by using Swiper React components like the following.



import { Swiper, SwiperSlide } from 'swiper/react'; // don't use this


Enter fullscreen mode Exit fullscreen mode

According to the swiper documentation, this way is no longer recommended as Swiper React components are likely to be removed in the future.
Swiper recommends using their Web components instead and that's what we will be using.

Web components
Web Components are a set of standardized technologies that allow developers to create and use reusable custom elements in web applications. Web components will allow you to write your own HTML elements and easily reuse them without using a framework.

For example, with Web components, you can create your own element like <validated-input> which takes the normal HTML input and add CSS and JavaScript to it so that whenever used it will come with validation, error messages, and styles out of the box. For more information about Web components check the following article.

Swiper has created their own custom elements that are preconfigured to provide the swiping logic out of the box with a syntax that looks like you are writing normal HTML

Create a file called MySwiper.jsx and add the following code



import { register } from "swiper/element/bundle";
register();

const MySwiper = () => {
  return (
    <swiper-container navigation="true" pagination="true">
      <swiper-slide class="blue-slide">Slide 1</swiper-slide>
      <swiper-slide class="yellow-slide">Slide 2</swiper-slide>
      <swiper-slide class="green-slide">Slide 3</swiper-slide>
    </swiper-container>
  );
};

export default MySwiper;


Enter fullscreen mode Exit fullscreen mode

In the top two lines, we are importing the register function from the swiper package which allows us to use the Swiper element in our component, and we register them by invoking the register() function.

  • The <swiper-container> is the Swiper custom element (Web component) which is used as the wrapper (parent) of our Swiper slides
  • The navigation = "true" attribute is used to add navigation arrows so that we can navigate between slides.
  • The pagination="true" attribute is used to add the pagination bullets which indicates the slide we are currently on.
  • The swiper-slide is another swiper custom element that creates individual slide
  • The class attribute is the same as a normal HTML class used to add CSS styles to the element.Notice that we are using class not className even if it's in React (you can read more about using web components in React projects in The React documentation).

I am using those classes (blue-slide, yellow-slide, and green-slide) to add different background colors for each slide. and centering the slide label text. You can find the current CSS HERE

After adding those styles and importing MySwiper component in the entry of the app you should have the following results in the browser.

Basic swiper

As you may have noticed the color of the current navigation buttons is not clear and the pagination bullets are too small to see therefore we need to customize them.

By default, swiper provides the following CSS classes

  • swiper-pagination-bullet: for styling pagination bullets
  • swiper-pagination-bullet-active: for styling the active pagination bullet
  • .swiper-button-next: for styling the next (right) navigation arrow
  • .swiper-button-prev: for styling the previous (left) navigation arrow

But the catch here is that trying to add our custom CSS to these classes directly from our CSS files will not work because the web components are kind of isolated in their own DOM therefore our CSS styles may not be able to reach them.

To add custom styles we will need to inject styles directly into the component itself and we will use React refs for it.

Update MySwiper component to be like the following



import { useEffect, useRef } from "react";
import { register } from "swiper/element/bundle";
register();

const MySwiper = () => {
  const swiperRef = useRef(null);

  useEffect(() => {
    const swiperContainer = swiperRef.current;
    const params = {
      navigation: true,
      pagination: true,
    };

    Object.assign(swiperContainer, params);
    swiperContainer.initialize();
  }, []);

  return (
    <swiper-container ref={swiperRef} init="false"> 
      <swiper-slide class="blue-slide">Slide 1</swiper-slide>
      <swiper-slide class="yellow-slide">Slide 2</swiper-slide>
      <swiper-slide class="green-slide">Slide 3</swiper-slide>
    </swiper-container>
  );
};

export default MySwiper;


Enter fullscreen mode Exit fullscreen mode

This is the exact same configuration we had earlier but we changed the way we are accessing the swiper container.

  • First we introduced the React useRef hook, which is allowing us to access the swiper container DOM node. We are using useEffect in this process to access the swiper container after React has finished rendering the page (so that it will not be null) You can read more about manipulating the DOM with refs HERE

  • We created the params object and we moved the navigation and pagination configurations we had earlier there. From now on all customizations to our swiper will live in this object.

  • After defining our swiper configurations we copy them to the swiper container using the Object.assign() method

  • Notice how we added the attribute init="false" to make sure that the swiper will not render before have applied our custom configuration.

  • swiperContainer.initialize(): we finally initialize the swiper after applying our configurations.

Adding custom styles

The Swiper container receives another property injectStyles which is an array of strings where each string will contain CSS styles to apply on Swiper elements

To apply the custom styles add the following changes (I am only mentioning a part of the previous file that I changed everything else stays the same)



  useEffect(() => {
    const swiperContainer = swiperRef.current;
    const params = {
      navigation: true,
      pagination: true,
      // These are new...
      injectStyles: [
        `
          .swiper-button-next,
          .swiper-button-prev {
            background-color: white;
            padding: 8px 16px;
            border-radius: 100%;
            border: 2px solid black;
            color: red;
          }
          .swiper-pagination-bullet{
            width: 40px;
            height: 40px;
            background-color: red;
          }
      `,
      ],
    };

    Object.assign(swiperContainer, params);
    swiperContainer.initialize();
  }, []);


Enter fullscreen mode Exit fullscreen mode

We just introduced custom styles where we are targeting both the next and prev buttons (using the CSS classes I mentioned earlier) and add change their color to red and adding the white circled background with a black border.

On the pagination bullet, we are increasing the width and height to be 40px and changing their color to be red

After applying these changes you should have the following results in the browser

Result after customising swiper

You can find the current progress in this Github repo

Bonus

What if you want to change the arrows and use completely custom ones? You can achieve this using the following code



  useEffect(() => {
    const swiperContainer = swiperRef.current;
    const params = {
      navigation: true,
      pagination: true,
      //add this
      injectStyles: [
        `
          .swiper-button-next,
          .swiper-button-prev {
            background-color: white;
            background-position: center;
            background-size: 40px;
            background-repeat: no-repeat;
            padding: 8px 16px;
            border-radius: 100%;
            border: 2px solid black;
            color: red;
          }

          .swiper-button-prev {
            background-image: url("/box-arrow-in-left.svg");
          }

          .swiper-button-next {
            background-image: url("/box-arrow-in-right.svg");
          }

          .swiper-button-next::after,
          .swiper-button-prev::after {
            content: "";
          }

          .swiper-pagination-bullet{
            width: 40px;
            height: 40px;
            background-color: red;
          }
      `,
      ],
    };

    Object.assign(swiperContainer, params);
    swiperContainer.initialize();
  }, []);


Enter fullscreen mode Exit fullscreen mode

We are giving the next and prev buttons their custom svg icons (I downloaded the svg icons added them in the public folder of the project) by setting them as the background image and removing background-repeat and setting the background size and background-position.



 .swiper-button-next::after,.swiper-button-prev::after {
    content: "";
 }


Enter fullscreen mode Exit fullscreen mode

This part is for hiding the default icons provided by Swiper itself since we are using our custom icons

Here is the result after adding custom arrows

results after adding custom arrows

And that's it, that's how you customize the navigation buttons and pagination bullets using Swiper web components. Hope this helps.

You can find the final code in the Github Repo

Top comments (13)

Collapse
 
marcosriani profile image
Marcos Cabrini Riani dos Reis

Thank you very much for this post, it really helped me as the current changes on Swiper js are not very documented out there.

Thanks a lot.

Collapse
 
melaskar profile image
melaskar

Excellent explanation, I was looking for just this and I could not get documentation to explain it. Thanks a lot

Collapse
 
arruday profile image
Felipe Arruda

thank uuuuuu

Collapse
 
forestdean profile image
John Walton

This has been really helpful. I was hoping to recreate an old jQuery carousel I set up with Malsup Cycle2. It was possible to set up custom controls outside of the slideshow container. Is this possible with Slider or have I reached a dead-end?

I tried using the swiper-button class on external button elements but there was no success.

Collapse
 
ivadyhabimana profile image
Ivad Yves HABIMANA

Hi @forestdean I think that's possible. You can actually control the slider programmatically by using the swiper.slideNext() and slider.slidePrev() and many other methods.
Here is the link to the the methods in the Swiper API swiperjs.com/swiper-api#method-swi...

Collapse
 
drprime01 profile image
DrPrime01

Hello, nice article
However, I'm getting swiperContainer.initialize is not a function. How did you manage to escape that?

Collapse
 
ivadyhabimana profile image
Ivad Yves HABIMANA

I'm not sure but I think that following the mentioned in the article steps 100% should get everything to work. If it's not working for you, can you try to log the swiperContainer and see what object do you have in your case

Collapse
 
artemshchirov profile image
Artem Shchirov • Edited

Hi! Thanks for the article. However, I am also stuck with this error in my Next.js TypeScript project: "swiperContainer.initialize is not a function."

When I use console.log(swiperContainer), it returns an HTML element.

<div class="swiper-slide" role="group" aria-label="3 / 3" style="width: 1160px; opacity: 0; transform: translate3d(-2320px, 0px, 0px);">
<img alt="Slide" loading="lazy" width="2731" height="4096" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_4.e3bfeac7.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_4.e3bfeac7.jpg&amp;w=3840&amp;q=75">
</div>
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
ivadyhabimana profile image
Ivad Yves HABIMANA

I just confirmed and everything still works even using next js I have recreated it in this code sandbox here codesandbox.io/p/sandbox/intellige...

Can you confirm that you are following all the steps?

Thread Thread
 
ivadyhabimana profile image
Ivad Yves HABIMANA
Collapse
 
stevesimms profile image
Steven Simms

I have to thank you for this.I was able to adjust this for my Angular appwith minimal effort.
Thansk Again!!!

Collapse
 
guipaiva profile image
Guilherme Paiva

Hi! First of all thank you so much for this post, helped me a lot!

I'm trying to customize my pagination to be clickable. How would I do that using the params object?

Thanks again!

Collapse
 
guipaiva profile image
Guilherme Paiva

Nevermind, I got it hahah

const params = {
    pagination: {
      clickable: true,
    },
    injectStyles:
Enter fullscreen mode Exit fullscreen mode