Weird behavior of Swiper.js in Next.js 13 & 14
Check that this is really a bug
- [x] I confirm
Reproduction link
Bug description
I don't know why I'm experiencing this behavior when I use Swiper in Next.js! Before the initial client-side rendering (SSR mode), I get an unstable Swiper style, as seen in these images. I don't know how to fix this on the first initial mount on every refresh.
Expected Behavior
"I don't want to see the flickering issue at the first refresh. It's not normal."
Actual Behavior
No response
Swiper version
11.1.4
Platform/Target and Browser Versions
chrome lasteat version
Validations
- [X] Follow our Code of Conduct
- [X] Read the docs.
- [X] Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
- [X] Make sure this is a Swiper issue and not a framework-specific issue
Would you like to open a PR for this bug?
- [ ] I'm willing to open a PR
Adding the cdn swiper files in document.tsx, helped me resolve this issue:
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
Nuxt 3 same problem
same problem
The flicker appears to stem from Swiper setting each slide's width/height after hydration, but not before on the server.
A solution to consider would be swiper offering some way to manually handle element height/width so we can seamlessly preserve the values from the server to the client.
Adding the cdn swiper files in document.tsx, helped me resolve this issue:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" /> <script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
I use app router from nextjs 14, placed it like this, but it does not solve the problem...
const RootLayout: React.FC<Props> = ({ children }) => {
return (
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
</head>
<body>
{children}
</body>
</html>
)
}
Adding the cdn swiper files in document.tsx, helped me resolve this issue:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" /> <script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>I use app router from nextjs 14, placed it like this, but it does not solve the problem...
const RootLayout: React.FC<Props> = ({ children }) => { return ( <html> <head> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" /> <script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script> </head> <body> {children} </body> </html> ) }
I guess you need to use the hijacked script from Next check this out https://nextjs.org/docs/app/building-your-application/optimizing/scripts#application-scripts
Edit: this is for app router
Adding the cdn swiper files in document.tsx, helped me resolve this issue:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" /> <script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>I use app router from nextjs 14, placed it like this, but it does not solve the problem...
const RootLayout: React.FC<Props> = ({ children }) => { return ( <html> <head> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" /> <script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script> </head> <body> {children} </body> </html> ) }I guess you need to use the hijacked script from Next check this out https://nextjs.org/docs/app/building-your-application/optimizing/scripts#application-scripts
Edit: this is for app router
I tried, thanks, but it didn't help unfortunately...
@isrezaei The issue arises because Swiper calculates and assigns each slide's width and margin only after the JavaScript loads. This can cause layout shifts, especially in server-side rendering (SSR) environments where initial styles are applied inconsistently.
To prevent this, a workaround could involve pre-setting the slide widths and margins in CSS based on the expected layout, so the layout is more stable before Swiper's JavaScript adjusts the slides. Additionally, if the layout shifts are too significant, consider implementing a custom solution to handle these styles on the server side.
livr.net/npm/swiper@11/swiper-bundle.min.js">
Do you have any working solution for that?
same issue here
FWIW, I tried a hacky solution by adding margin-left to the swiperslide and it seems to be working fine for now.
<Swiper slidesPerView="auto" freeMode={true} modules={[FreeMode]}>
{cards.map((card, index) => (
<SwiperSlide
key={index}
style={{
width: "auto",
marginLeft: index === 0 ? '0px' : '16px',
}}
>
{card}
</SwiperSlide>
))}
</Swiper>
Hopefully, someone can provide an actual solution