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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 13:45:31