react: Edge 18 & IE 11 server mismatch with SVG icons

Do you want to request a feature or report a bug?

bug

What is the current behavior?

React raises a warning:

Capture d’écran 2019-03-22 à 00 40 00

https://codesandbox.io/s/k91nr3xzy5

import React from "react";

export default () => (
  <div>
    2
    <svg>
      <path d="M0 0h24v24H0z" />
    </svg>
  </div>
);

What is the expected behavior?

No warning

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

React: 16.8.4 Browser: Edge 18

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 27
  • Comments: 20 (1 by maintainers)

Most upvoted comments

In react v18.2.0, This problem still exists 😕

Same experience: react-icons, MUI, SSR in pages/ dir with NextJS 13.2.1 & React 18.2.0

I am on react@16.13.1 and it’s still a problem.

I was able to find a way to handle this:

<Button variant="ghost" size="sm" onClick={() => { setTheme(theme === "light" ? "dark" : "light") }}>
  {loaded ?
    (theme === "light" ? <MoonIcon /> : <SunIcon />)
    : (<span className="animate-pulse">...</span>)
  }
</Button>

code with context, if needed:

'use client'

import React, { useEffect, useState } from 'react'
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"
import { SunIcon, MoonIcon } from "@/components/ui/icons"

export default function NavBar() {
  const { theme, setTheme } = useTheme()
  const [loaded, setLoaded] = useState(false);
  useEffect(() => {
    setLoaded(true);
  }, [setLoaded]);

  return (
    <div className='flex items-center fixed top-0 left-0 right-0'>
      <div className='px-3'>Bonates.com</div>
      <div className='flex-grow' />
      <Button variant="ghost" size="sm" onClick={() => { setTheme(theme === "light" ? "dark" : "light") }}>
        {loaded ?
          (theme === "light" ? <MoonIcon /> : <SunIcon />)
          : (<span className="animate-pulse">...</span>)
        }
      </Button>
    </div>
  )
}

+1 with SVG icon exported from React component and rendered in NextJS

I am trying to use it in my DarkMode.tsx with Next.js & I face the same error:

react_devtools_backend.js:2560 Warning: Prop d did not match. Server: “M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z” Client: “M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z” at path at svg at MoonIcon at button at div at DarkMode

DarkMode.tsx

import { MoonIcon, SunIcon } from '@heroicons/react/solid'

import { useStore } from '@/store/index'

export const DarkMode = () => {
	const { theme, setTheme } = useStore()

	return (
		<div className="cursor-pointer">
			{theme === 'dark' && (
				<button
					className="focus:outline-none"
					title="Activate light mode"
					onClick={() => {
						console.log(theme)
						setTheme('light')
					}}
				>
					<MoonIcon className="w-8 h-8" />
				</button>
			)}
			{theme === 'light' && (
				<button
					className="focus:outline-none"
					title="Activate dark mode"
					onClick={() => {
						setTheme('dark')
					}}
				>
					<SunIcon className="w-8 h-8" />
				</button>
			)}
		</div>
	)
}

Edit: No worries. I wasn’t using observer so that solved it 😃

@gaearon It’s a shame I have only been contributing issues to the React project, in all these years. This issue is not very important. I doesn’t break the UI. It was more about moving an issue reported on Material-UI closer to its origin than anything else. Me working on #13838 could be more valuable for the community.

For anyone looking for a workaround for the time being, disabling hydration on inline SVG elements seems to do the trick. This obviously comes with downsides of its own, namely in that it’s only really applicable to static SVGs, but here’s the relevant TypeScript code for a component that disables hydration:

import {
	FC,
	HTMLProps,
	ReactNode,
	useEffect,
	useLayoutEffect,
	useReducer,
	useRef
} from "react";

type NoHydrationProps = {
	readonly children: ReactNode;
	readonly parent?: keyof JSX.IntrinsicElements;
} & Omit<HTMLProps<HTMLElement>, "dangerouslySetInnerHTML">;

const reducer = () => true;

const isBrowser = typeof window !== "undefined";

const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect;

export const NoHydration = ({
	children,
	parent = "div",
	...props
}: NoHydrationProps) => {
	const ref = useRef<HTMLElement>(null);

	const [isHydrated, hydrate] = useReducer(reducer, !isBrowser);

	useIsomorphicLayoutEffect(() => {
		if (ref.current?.hasChildNodes()) {
			return;
		}

		hydrate();
	}, []);

	const ParentElement = parent as unknown as FC<HTMLProps<HTMLElement>>;

	if (!isHydrated) {
		return (
			<ParentElement
				ref={ref}
				suppressHydrationWarning
				dangerouslySetInnerHTML={{ __html: "" }}
				{...props}
			/>
		);
	}

	return (
		<ParentElement
			ref={ref}
			// `display: contents` ensures the parent element does not produce
			// a CSS box for itself, seeing as the parent is only needed for the
			// purpose of preventing client-side hydration of the children.
			style={{ display: "contents" }}
			{...props}
		>
			{children}
		</ParentElement>
	);
};

@gaearon IE, Edge do svg paths normalization. It’s similiar to

https://github.com/facebook/react/blob/b1a03dfdc8e42d075422556553ffe59868150e95/packages/react-dom/src/client/ReactDOMComponent.js#L141-L149

and can be archived in the same way. Any objections?