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
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
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;
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 usingclass
notclassName
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.
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;
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 usinguseEffect
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 HEREWe 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();
}, []);
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
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();
}, []);
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: "";
}
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
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)
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.
Excellent explanation, I was looking for just this and I could not get documentation to explain it. Thanks a lot
thank uuuuuu
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.Hi @forestdean I think that's possible. You can actually control the slider programmatically by using the
swiper.slideNext()
andslider.slidePrev()
and many other methods.Here is the link to the the methods in the Swiper API swiperjs.com/swiper-api#method-swi...
Hello, nice article
However, I'm getting swiperContainer.initialize is not a function. How did you manage to escape that?
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
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.
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?
@artemshchirov @drprime01
I have to thank you for this.I was able to adjust this for my Angular appwith minimal effort.
Thansk Again!!!
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!
Nevermind, I got it hahah