Next.js中img标签设置src属性触发服务端警告的原因排查
Next.js 原生img标签加src属性触发Hydration警告问题
问题现象
- 给原生
<img>标签添加外链src属性时,控制台抛出Extra attributes from the server: style警告,移除src属性后警告消失 - 已在
next.config.js中配置images.domains将对应图片域名加入允许列表,配置片段如下:
module.exports = { images: { domains: [ 'themekit.dev',
- 触发警告的img写法:
<img src={'https://themekit.dev/shared/thumbs/wide.png'} alt="cover image" />
- 无警告的img写法:
<img alt="cover image" />
相关组件完整代码
import Image from 'next/image' import React, { useState } from 'react' import styles from '../styles/ImageUpload.module.css' import { updateClientAndServer, uploadImage, useValueFromKeyPath, } from 'tikexModule/slice' import { useAppDispatch } from 'tikexModule/hooks/useAppDispatch' import { v4 as uuid } from 'uuid' interface ImageUploadProps { bucket: string keyPath: string[] title?: string srcFallback?: string width?: number height?: number } const ImageUpload2 = ({ bucket, keyPath, title, srcFallback, width, height, }: ImageUploadProps) => { const dispatch = useAppDispatch() const keyPath2 = keyPath const imgId = useValueFromKeyPath(keyPath) return ( <> <input type="file" id="actual-btn" onChange={(e) => { const imgIdNew = uuid() dispatch( uploadImage({ props: { imageKey: imgIdNew, bucket: bucket, imageKeyPrev: imgId, }, file: e.target.files[0], andThen: () => { dispatch( updateClientAndServer({ keyPath: keyPath, value: imgIdNew, operation: 'setValue', valuePrev: imgId, }) ) }, }) ) }} hidden /> <label className={title ? 'btn btn-sm btn-circle' : 'img-box'} htmlFor="actual-btn" > <img src={'https://themekit.dev/shared/thumbs/wide.png'} alt="cover image" /> </label> </> ) } export default ImageUpload2
控制台完整警告栈
next-dev.js?3515:25 Warning: Extra attributes from the server: style at img at label at ImageUpload2 (webpack-internal:///./tikexModule/components/ImageUpload2.tsx:17:24) at div at div at div at div at div at SurveySchema (webpack-internal:///./tikexModule/pages/dashboard/organizations/[organizationId]/posts/[postId]/index.tsx:22:72) at div at Page (webpack-internal:///./tikexModule/components/Dashboard/Page/index.tsx:15:26) at div at div at section at main at Layout (webpack-internal:///./components/Layout.tsx:11:26) at DashboardLayout (webpack-internal:///./tikexModule/components/Dashboard/DashboardLayout.tsx:22:26) at DashboardProvider (webpack-internal:///./tikexModule/components/Dashboard/DashboardContext.tsx:46:26) at WrapperComponent at AppProvider (webpack-internal:///./tikexModule/AppContext.tsx:50:26) at Provider (webpack-internal:///./node_modules/react-redux/es/components/Provider.js:13:3) at MyApp (webpack-internal:///./pages/_app.tsx:96:27) at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/client.js:8:20746) at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/client.js:8:23395) at Container (webpack-internal:///./node_modules/next/dist/client/index.js:323:9) at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:849:26) at Root (webpack-internal:///./node_modules/next/dist/client/index.js:973:27)
问题原因
这个警告和你配置的images.domains没有任何关系:images.domains配置只对Next.js内置的next/image组件生效,你当前使用的是原生<img>标签,不会走Next.js的图片优化逻辑,相关配置自然不会对它生效。
警告本质是React水合(Hydration)不匹配:服务端渲染返回的img标签没有内联style属性,但客户端水合阶段,浏览器内置逻辑、你引入的第三方图片处理脚本(比如懒加载、自适应脚本)或者浏览器扩展,会自动给带有效src的img标签添加内联style属性,React对比服务端和客户端DOM属性时发现差异,就会抛出该警告。
没有src属性的img标签不会被这类逻辑识别处理,不会产生属性差异,因此不会触发警告。
解决方案
按优先级选择任意一种即可:
- 优先使用Next.js内置的Image组件:你的代码中已经引入了
next/image但未使用,替换为Image组件即可复用已配置的域名白名单,同时享受Next.js的图片优化能力,参考写法如下:
// 给父级label元素添加position: relative样式即可实现图片铺满效果 <Image src={'https://themekit.dev/shared/thumbs/wide.png'} alt="cover image" fill sizes="100vw" />
- 如果必须使用原生img标签,给标签添加
suppressHydrationWarning={true}属性,告知React忽略该元素的水合属性差异警告:
<img src={'https://themekit.dev/shared/thumbs/wide.png'} alt="cover image" suppressHydrationWarning={true} />
- 排查全局注入脚本、浏览器扩展:如果是项目内自定义的图片处理脚本给img添加了内联样式,可以调整脚本执行时机,等页面水合完成后再执行属性注入,从根源避免水合阶段的DOM差异。
内容的提问来源于stack exchange,提问作者János
相关产品推荐
相关产品推荐

