You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js集成Swiper断点配置引发Hydration不匹配错误的解决方法

解决Next.js中Swiper断点导致的Hydration不匹配问题

以下是实现Swiper轮播的代码:

const breakpoints = {
    0: {
        slidesPerView: 1,
    },
    576: {
        slidesPerView: 1,
    },
    768: {
        slidesPerView: 2,
    },
    992: {
        slidesPerView: 3,
    },
    1199: {
        slidesPerView: 4,
    },
}
<Swiper
    modules={[Navigation]}
    spaceBetween={30}
    slidesPerView={4}
    loop={true}
    navigation={{
        nextEl: sliderNextEl.current,
        prevEl: sliderPrevEl.current,
    }}
    breakpoints={breakpoints}
    className="sg-portfolio-active swiper-container"
>
    <SwiperSlide>Hello 1</SwiperSlide>
    <SwiperSlide>Hello 2</SwiperSlide>
    <SwiperSlide>Hello 3</SwiperSlide>
</Swiper>

在桌面模式(>=1199px)下运行正常,但切换到移动端视图并刷新页面时,Next.js抛出如下错误:

Prop `data-swiper-slide-index` did not match. Server: "0" Client: "3"

Error: Hydration failed because the initial UI does not match what was rendered on the server.

注释掉Swiper组件中的breakpoints={breakpoints}配置后问题消失,尝试用state存储breakpoints传入Swiper未生效。

环境版本:

swiper: ^8.4.5
NextJs: 13.1.1
ReactJs: 18.2.0

解决方法

1. 仅在客户端渲染Swiper组件

Next.js服务端渲染时无法获取客户端窗口尺寸,导致服务端与客户端根据断点计算的幻灯片数量不一致,直接禁用Swiper组件的SSR:

import dynamic from 'next/dynamic';

// 动态导入Swiper和SwiperSlide,禁用SSR
const Swiper = dynamic(() => import('swiper/react').then(mod => mod.Swiper), {
  ssr: false,
});
const SwiperSlide = dynamic(() => import('swiper/react').then(mod => mod.SwiperSlide), {
  ssr: false,
});

之后正常使用组件即可,组件仅在客户端渲染,避免服务端与客户端DOM不匹配。

2. 客户端挂载后再设置断点

如果需要保留SSR,可在组件挂载后(仅客户端环境)再设置断点,确保服务端渲染时使用默认配置:

import { useState, useEffect } from 'react';

export default function YourComponent() {
  const [clientBreakpoints, setClientBreakpoints] = useState(null);

  useEffect(() => {
    // 仅在客户端设置断点
    setClientBreakpoints({
      0: { slidesPerView: 1 },
      576: { slidesPerView: 1 },
      768: { slidesPerView: 2 },
      992: { slidesPerView: 3 },
      1199: { slidesPerView: 4 },
    });
  }, []);

  return (
    <Swiper
      modules={[Navigation]}
      spaceBetween={30}
      slidesPerView={4}
      loop={true}
      navigation={{
        nextEl: sliderNextEl.current,
        prevEl: sliderPrevEl.current,
      }}
      breakpoints={clientBreakpoints}
      className="sg-portfolio-active swiper-container"
    >
      <SwiperSlide>Hello 1</SwiperSlide>
      <SwiperSlide>Hello 2</SwiperSlide>
      <SwiperSlide>Hello 3</SwiperSlide>
    </Swiper>
  );
}

服务端渲染时breakpoints为null,Swiper使用默认的slidesPerView=4,客户端挂载后再应用断点配置,仅会触发一次重渲染,不会导致Hydration错误。

3. 延迟开启loop模式

loop模式下Swiper会复制幻灯片,服务端与客户端的幻灯片数量计算差异会导致索引不匹配,可在客户端挂载后再开启loop:

import { useState, useEffect } from 'react';

export default function YourComponent() {
  const [isClient, setIsClient] = useState(false);
  const [clientBreakpoints, setClientBreakpoints] = useState(null);

  useEffect(() => {
    setIsClient(true);
    setClientBreakpoints({
      0: { slidesPerView: 1 },
      576: { slidesPerView: 1 },
      768: { slidesPerView: 2 },
      992: { slidesPerView: 3 },
      1199: { slidesPerView: 4 },
    });
  }, []);

  return (
    <Swiper
      modules={[Navigation]}
      spaceBetween={30}
      slidesPerView={4}
      loop={isClient}
      navigation={{
        nextEl: sliderNextEl.current,
        prevEl: sliderPrevEl.current,
      }}
      breakpoints={clientBreakpoints}
      className="sg-portfolio-active swiper-container"
    >
      <SwiperSlide>Hello 1</SwiperSlide>
      <SwiperSlide>Hello 2</SwiperSlide>
      <SwiperSlide>Hello 3</SwiperSlide>
    </Swiper>
  );
}

结合断点的客户端设置,彻底避免服务端与客户端的渲染差异。


内容的提问来源于stack exchange,提问作者Tajmirul Islam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 03:30:59