diff --git a/packages/gatsby-plugin-image/gatsby-browser.js b/packages/gatsby-plugin-image/gatsby-browser.js deleted file mode 100644 index 7ca0ef15141ce..0000000000000 --- a/packages/gatsby-plugin-image/gatsby-browser.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from "react" -import { LaterHydrator } from "." - -export function wrapRootElement({ element }) { - return {element} -} diff --git a/packages/gatsby-plugin-image/package.json b/packages/gatsby-plugin-image/package.json index 9321b7271fcc7..7d708ed899f8e 100644 --- a/packages/gatsby-plugin-image/package.json +++ b/packages/gatsby-plugin-image/package.json @@ -27,7 +27,7 @@ "esmodule": "dist/gatsby-image.modern.js", "browser": { "./dist/gatsby-image.js": "./dist/gatsby-image.browser.js", - "./dist/gatsby-image.modern.js": "./dist/gatsby-image.browser.modern.js" + "./dist/gatsby-image.module.js": "./dist/gatsby-image.browser.module.js" }, "files": [ "dist/*", @@ -93,4 +93,4 @@ }, "author": "Matt Kane ", "license": "MIT" -} \ No newline at end of file +} diff --git a/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx b/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx index 25fbcde9c147d..154d8617a5b75 100644 --- a/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx +++ b/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx @@ -1,8 +1,6 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ /** * @jest-environment jsdom */ - import React from "react" import { render, waitFor } from "@testing-library/react" import * as hooks from "../hooks" @@ -145,10 +143,13 @@ describe(`GatsbyImage browser`, () => { { container: beforeHydrationContent, hydrate: true } ) - const placeholder = await waitFor(() => - container.querySelector(`[data-placeholder-image=""]`) + const placeholder = await waitFor( + () => + container.querySelector(`[data-placeholder-image=""]`) as HTMLElement ) - const mainImage = container.querySelector(`[data-main-image=""]`) + const mainImage = container.querySelector( + `[data-main-image=""]` + ) as HTMLElement expect(placeholder).toBeDefined() expect(mainImage).toBeDefined() diff --git a/packages/gatsby-plugin-image/src/components/gatsby-image.browser.tsx b/packages/gatsby-plugin-image/src/components/gatsby-image.browser.tsx index de0f809a680b6..a008c015e2e45 100644 --- a/packages/gatsby-plugin-image/src/components/gatsby-image.browser.tsx +++ b/packages/gatsby-plugin-image/src/components/gatsby-image.browser.tsx @@ -6,7 +6,11 @@ import { useLayoutEffect, useRef, } from "react" -import { getWrapperProps, gatsbyImageIsInstalled } from "./hooks" +import { + getWrapperProps, + gatsbyImageIsInstalled, + hasNativeLazyLoadSupport, +} from "./hooks" import { getSizer } from "./layout-wrapper" import { propTypes } from "./gatsby-image.server" import type { @@ -56,25 +60,30 @@ export interface IGatsbyImageData { placeholder?: Pick } -const GatsbyImageHydrator: FC = function GatsbyImageHydrator( - props -) { - const { width, height, layout } = props.image +const GatsbyImageHydrator: FC = function GatsbyImageHydrator({ + as = `div`, + image, + style, + backgroundColor, + className, + class: preactClass, + onStartLoad, + onLoad, + onError, + ...props +}) { + const { width, height, layout } = image const { style: wStyle, className: wClass, ...wrapperProps } = getWrapperProps(width, height, layout) const root = useRef() - const cacheKey = useMemo( - () => JSON.stringify(props.image.images), - [props.image.images] - ) + const cacheKey = useMemo(() => JSON.stringify(image.images), [image.images]) - let className = props.className // Preact uses class instead of className so we need to check for both - if (props.class) { - className = props.class + if (preactClass) { + className = preactClass } const sizer = getSizer(layout, width, height) @@ -98,23 +107,23 @@ const GatsbyImageHydrator: FC = function GatsbyImageHydrator( const ssrImage = root.current.querySelector( `[data-gatsby-image-ssr]` ) as HTMLImageElement - if (ssrImage) { + if (ssrImage && hasNativeLazyLoadSupport()) { if (ssrImage.complete) { // Trigger onStartload and onLoad events - props?.onStartLoad?.({ + onStartLoad?.({ wasCached: true, }) - props?.onLoad?.({ + onLoad?.({ wasCached: true, }) } else { - document.addEventListener(`load`, function onLoad() { - document.removeEventListener(`load`, onLoad) + document.addEventListener(`load`, function onLoadListener() { + document.removeEventListener(`load`, onLoadListener) - props?.onStartLoad?.({ + onStartLoad?.({ wasCached: true, }) - props?.onLoad?.({ + onLoad?.({ wasCached: true, }) }) @@ -134,9 +143,9 @@ const GatsbyImageHydrator: FC = function GatsbyImageHydrator( renderImageToStringPromise.then( ({ renderImageToString, swapPlaceholderImage }) => { root.current.innerHTML = renderImageToString({ - image: props.image.images, isLoading: true, isLoaded: imageCache.has(cacheKey), + image, ...props, }) @@ -147,10 +156,10 @@ const GatsbyImageHydrator: FC = function GatsbyImageHydrator( root.current, cacheKey, imageCache, - props.style, - props.onStartLoad, - props.onLoad, - props.onError + style, + onStartLoad, + onLoad, + onError ) } }) @@ -167,35 +176,35 @@ const GatsbyImageHydrator: FC = function GatsbyImageHydrator( cleanupCallback() } } - }, [props.image.images]) + }, [image]) - // We need to run this effect before browser has paint to make sure our html is set so no flickering happens - // + // useLayoutEffect is ran before React commits to the DOM. This allows us to make sure our HTML is using our cached image version useLayoutEffect(() => { if (imageCache.has(cacheKey) && renderImage) { root.current.innerHTML = renderImage({ - image: props.image.images, isLoading: imageCache.has(cacheKey), isLoaded: imageCache.has(cacheKey), + image, ...props, }) // Trigger onStartload and onLoad events - props?.onStartLoad?.({ + onStartLoad?.({ wasCached: true, }) - props?.onLoad?.({ + onLoad?.({ wasCached: true, }) } - }, [props.image.images]) + }, [image]) - return createElement(props.as || `div`, { + // By keeping all props equal React will keep the component in the DOM + return createElement(as, { ...wrapperProps, style: { ...wStyle, - ...props.style, - backgroundColor: props.backgroundColor, + ...style, + backgroundColor, }, className: `${wClass}${className ? ` ${className}` : ``}`, ref: root,