SSR and responsive mode
I already open this issue on the nextjs repo but it seems to be a problem with react-slick.
Describe the bug When I use next.js with react-slick and responsive options, there is a bug during the rehydration phase, the src attribute of the images are mixed between the slides.
The error message is Warning: Prop src did not match. Server: "http://via.placeholder.com/350x150?text=7" Client: "http://via.placeholder.com/350x150?text=8"
To Reproduce I made an example from examples folder : https://github.com/padupuy/next.js/tree/feature/with-react-slick
The code is right here : https://github.com/padupuy/next.js/blob/feature/with-react-slick/examples/with-react-slick/pages/index.js
Expected behavior The image sources must not be mixed
Screenshots

System information
- OS: macOS High Sierra 10.13.4
- Browser (if applies) : at least chrome and safari
- Version of Next.js: at least 5.1 and 6.0.2
I am facing a similar issue with SSR. Were you able to find a fix?
Is any solution to prevent this bug? Thank you.
I don't know if is the best approach but I solved this problem by using dynamic import
import dynamic from 'next/dynamic'
const SliderComponentWithNoSSR = dynamic(import('../components/SliderComponent'), {
ssr: false
})
export default () =>
<div>
<SliderComponentWithNoSSR />
</div>
I too am having this same issue. For now I have to use another plugin since it's mixing up the content for me
+1
+1
+1
+1
Thank you @davidsanchez96
+1
+1
Following @davidsanchez96's lead, I still wanted the actual content to be rendered on both the server and the client, so this was my approach.
import React, { Component } from 'react';
import dynamic from 'next/dynamic';
import classNames from 'classnames';
class Slider extends Component {
state = {
isServer: true
};
componentDidMount() {
this.setState((state) => state.isServer && { isServer: false });
}
render() {
const { settings, className, children } = this.props;
const SliderRendered = dynamic(import('react-slick'), {
ssr: this.state.isServer
});
return (
<SliderRendered className={classNames('Slider', className)} {...settings}>
{children}
</SliderRendered>
);
}
}
export default Slider;
...or with hooks:
import React, { useState, useEffect } from 'react';
import dynamic from 'next/dynamic';
import classNames from 'classnames';
const Slider = ({ settings, className, children }) => {
const [isServer, setServerState] = useState(true);
const SliderRendered = dynamic(import('react-slick'), {
ssr: isServer
});
useEffect(() => void setServerState(false), []);
return (
<SliderRendered className={classNames('Slider', className)} {...settings}>
{children}
</SliderRendered>
);
};
export default Slider;
This is my solution:
import React, { PureComponent } from 'react';
import SlickSlider from 'react-slick';
class Slider extends PureComponent {
state = {
isClient: false
};
componentDidMount() {
this.setState((state) => { isClient: true });
}
render() {
const {
children,
responsive,
...rest
} = this.props;
const { isClient } = this.state;
return (
<SlickSlider
key={isClient ? 'client' : 'server'}
responsive={isClient ? responsive : null}
{...rest}
>
{children}
</SlickSlider>
);
}
}
export default Slider;
Well, I have this bug but I don't want a state for this. Is @akiran is willing to fix that ? Thanks.
@akiran any updates regarding this issue?
@akiran any updates?
This is my solution:
import React, { PureComponent } from 'react'; import SlickSlider from 'react-slick'; class Slider extends PureComponent { state = { isClient: false }; componentDidMount() { this.setState((state) => { isClient: true }); } render() { const { children, responsive, ...rest } = this.props; const { isClient } = this.state; return ( <SlickSlider key={isClient ? 'client' : 'server'} responsive={isClient ? responsive : null} {...rest} > {children} </SlickSlider> ); } } export default Slider;
This is my solution:
import React, { PureComponent } from 'react'; import SlickSlider from 'react-slick'; class Slider extends PureComponent { state = { isClient: false }; componentDidMount() { this.setState((state) => { isClient: true }); } render() { const { children, responsive, ...rest } = this.props; const { isClient } = this.state; return ( <SlickSlider key={isClient ? 'client' : 'server'} responsive={isClient ? responsive : null} {...rest} > {children} </SlickSlider> ); } } export default Slider;
Thanks for this solution @isBatak !
I can't quit figure out exactly in my head what happens here; the images are still being rendered on server-side but it works because .......?
Hi, I had the same issue. The workaround works great.
@silksil I think it works because when we use different keys react won't hydrate the state from the server but creates it from scratch on the client.
Setting responsive to null on server-side prevents from creating elements that are created on client side afterward.
@akiran any hint how we can fix it?
Hi @silksil, that trick is called two-pass rendering, you can find more info here https://reactjs.org/docs/react-dom.html#hydrate. In a nutshell, it will trigger second render on the client.
- render on server side (there is no window so media query detection won't work)
- hydration pass on client (window is available but if we change DOM in this phase hydration will fail)
- trigger second render on client by changing the key (in this phase DOM changes won't be a problem)
Overall, this problem can't be solved with JS because there is no window on server side and there is no way to detect window width. Only way to solve it is to generate pure CSS media queries and never touch the DOM structure.
Any updates?
@akiran any update on the issue ?
Inspired by the @isBatak solution, mine is using react hooks:
import { useState, useEffect } from 'react';
import SlickSlider from 'react-slick';
const Slider = ({ children, responsive, ...rest}) => {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, [])
return (
<SlickSlider
key={isClient ? 'client' : 'server'}
responsive={isClient ? responsive : null}
{...rest}
>
{children}
</SlickSlider>
);
}
export default Slider;
import React from "react";
import Slider from "react-slick";
const settings = {
dots: false,
infinite: false,
speed: 500,
slidesToShow: 4,
slidesToScroll: 1,
lazyLoad: false,
arrows: false,
responsive: [
{
breakpoint: 1280,
settings: {
slidesToShow: 3.125,
}
},
{
breakpoint: 992,
settings: {
slidesToShow: 2.125,
}
},
{
breakpoint: 680,
settings: {
slidesToShow: 1.75,
}
},
]
}
If I use this for SSR, it gives me warning like these two
Warning: Prop `style` did not match. Server: "width:100%;left:0%" Client: "width:128%;left:0%"
Warning: Prop `className` did not match. Server: "slick-slide slick-active" Client: "slick-slide"
What is the solution for this warning?
Hello, @rubenkuipers
To be honest, I need above thing urgently, I think that maybe react-slick has issue or I missed one option for SSR.
It'd be great if you advice me. Thank you
If you are using next js then just use dynamic import for that component where you used react-slick or if you are not using ssr then just use lazyload component and suspension .
Tried using dynamic import const SliderRendered = dynamic(import('react-slick'));
and <SliderRendered {...settings} but still mixes the content! Please help
import Slider from "react-slick" const ExampleComponent = () => { return ( <SliderRendered {...settings}> //your items </Slider> ); }
use below example when you are importing your slider component to page const ExampleComponent = dynamic(import('./ExampleComponent'));
There have same problem when use swiper/react in nextjs with responsive mode
// component swiper
import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperCore, { Navigation, Pagination } from 'swiper';
import 'swiper/swiper.min.css';
import 'swiper/components/pagination/pagination.min.css';
SwiperCore.use([Navigation, Pagination]);
<Swiper
navigation={{
nextEl: '.next',
prevEl: '.prev',
}}
breakpoints={{
1000: {
slidesPerView: 3,
},
0: {
slidesPerView: 1,
},
}}>
{data.map((item, index) => (
<SwiperSlide key={index}>
<article>
<img src={item.thumbnailUrl} />
<h2>{item.id}</h2>
</article>
</SwiperSlide>
))}
</Swiper>
//----------------------------------------------------
// index.js
const DynamicComponent = dynamic(() => import('../components/Swiper'), {
ssr: false,
});
export default function Home({ data }) {
return (
<div>
<DynamicComponent data={data} />
</div>
);
}
export async function getServerSideProps() {
const res = await fetch(
`https://jsonplaceholder.typicode.com/photos?_limit=10`
);
const data = await res.json();
return { props: { data } };
}
@anettwu Thanks for the hint