next-themes
next-themes copied to clipboard
next/image example does not work after the page refresh
I have added a few more code to wait for the client to render the component.
import Image from 'next/image'
import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'
function ThemedImage() {
const { resolvedTheme } = useTheme()
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
if (!mounted) {
return null
}
let src
switch (resolvedTheme) {
case 'light':
src = '/light.png'
break
case 'dark':
src = '/dark.png'
break
default:
src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
break
}
return <Image src={src} width={400} height={400} />
}
export default ThemedImage
It works fine but not perfect. I want the image to stay still. However, when I refresh the page, the image starts to flicker on every page render. Any solutions?
Can you share a minimal deployed reproduction?
@pacocoursey I've added a short clip to demonstrate the issue. This is what it looks like when the page has refreshed.
https://user-images.githubusercontent.com/6092023/204434855-cf447ebc-1c6f-4fce-9bf7-85edd243ec8b.mp4
The reason why I've upload the clip is because the CodeSandbox has a delay when I click the refresh button. But I'm pretty sure that you will get the point.
@pacocoursey Any ideas? 🤔
I have the same problem (I use tailwind too). After first page load/refresh, everything updates to dark mode but not the images.
I've been experiencing this problem too. Hoping to get help with it. What I've done (and baybe can throw a bit of help in here) is to use svg or png logos for the header, that way I can control with tailwind (for the SVG case) the color with light and dark themes, and if it's a PNG the background will be transparent (this doesn't work for all use cases though).
Okay, after a bit of investigation and thinking I think I have a solution, want to share it woth you guys so you can tell me if you think is okay:
I'm building a website where I'm displaying images based on the theme of the website. For components as the header and footer I'm using an SVG element to display the logo of the site. With this technique this specific case problem is solved, because I'm using Tailwind, which let's you set the light and dark colors with the currentTheme property as the following code shows:
<div className="text-blue-700 dark:text-blue-100">
<svg
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
</div>
The problem comes with images. The mismatch error occurs because in the server we don't have a certain way to know what's the theme the browser will have, so when rendering the component in the server, there will always be a chance that the image displayed in the server doesn't match the one shown in the client (that's why I think it's best to use SVG elements in this kind of apps whenever we can). But there are times where we cannot change the image for an SVG element, for those specific cases what I've done (and I want your opinion on this) is to not show the image at all when the page is not rendered in the client. I don't know if this can cause my page not to rank higher due to SEO or something like that, but with this I'm not getting errors in my project, here is a look at how my component looks like:
const ThemedImage: FC<{ lightImage: IImage; darkImage: IImage }> = ({
lightImage,
darkImage,
}) => {
const { resolvedTheme } = useTheme();
const [mounted, setMounted] = useState(false);
let srcImage: IImage;
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}
switch (resolvedTheme) {
case "light":
srcImage = lightImage;
break;
case "dark":
srcImage = darkImage;
break;
default:
srcImage = lightImage;
break;
}
return (
<Image
className="rounded-md"
src={srcImage.url}
width={srcImage.width}
height={srcImage.height}
alt={srcImage.description}
/>
);
};
export default ThemedImage;
What do you guys think about this solution?
@ismaeljtl Thanks for the response with some ideas. Let me go through your solution first and then I'll leave feedback. :) Btw, do you have a link for the reproduction?
Just ran into this, the solution by @ismaeljtl where in a useEffect
the mounted
state is set to true
, made it work for me.
It might be good to update the example in the readme like this.