Customizing Swiper.js prev/next arrow buttons and pagination bullets in React
Tags: react, webdev, javascript, tutorial
Published: 2023-04-08 18:24:12

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](https://vitejs.dev/guide/#scaffolding-your-first-vite-project) but you can also use [create-react-app](https://create-react-app.dev/docs/getting-started) or any other way you want.

You can find the React setup I will be using in [HERE](https://github.com/Yvad60/blog-code-references/tree/0016bddfba92656d41b17efb95f9939f19e4e64c) 

- A code editor: I will be using [Visual Studio Code](https://code.visualstudio.com/) 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
```bash
# For npm
npm install swiper
# For yarn
yarn add swiper
# For pnpm
pnpm install swiper

```
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.

```js
import { Swiper, SwiperSlide } from 'swiper/react'; // don't use this
```
According to the [swiper documentation](https://swiperjs.com/react), 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](https://kinsta.com/blog/web-components/). 

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
```js
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;
```

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](https://react.dev/reference/react-dom/components#custom-html-elements)).

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](https://github.com/Yvad60/blog-code-references/blob/413674128751648fdc8ee0d5da3c8a32c6bcba96/src/index.css)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cm09iafz1yzshsmt172f.gif)

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 

```js
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;
```
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](https://react.dev/learn/manipulating-the-dom-with-refs)

- 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)

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

```js
  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();
  }, []);
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ldf7a094r1x6poolcl89.gif)

You can find the current progress in this [Github repo](https://github.com/Yvad60/blog-code-references/tree/0400a1e911452e97b685cd6c961090b49c8e2311)

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

```js
  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();
  }, []);
```

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.

```css
 .swiper-button-next::after,.swiper-button-prev::after {
    content: "";
 }
```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9c5lyg5gya7blm8hz73.gif)

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](https://github.com/Yvad60/blog-code-references/tree/customise-swiper-arrow-and-bullets)