lorenzodejong / next-sanity-image

Utility for using responsive images hosted on the Sanity.io CDN with the Next.js image component.
MIT License
148 stars 19 forks source link

Placeholder for SVG causes the browser to download original image regardless of "blur" or "width" #17

Closed surjithctly closed 1 year ago

surjithctly commented 3 years ago

When an image requested from Sanity is an svg (eg: 600 x 600) and for placeholder svg is called as "blur" and "w=64" but since sanity does not process SVG, it downloads the whole image and causes "Defer Offscreen Image" warning on Lighthouse.

Can this plugin automatically skip placeholder if the format is in SVG even if blurUp is enabled?

lorenzodejong commented 3 years ago

Perhaps we can take this into consideration with the upcoming LQIP support in #8. I can imagine that Sanity already handles this from the base64 image which is present on the metadata field.

lorenzodejong commented 1 year ago

Support for blur up options has been dropped from this library in version 5.0.0. Check out the upgrade guide for more information https://github.com/bundlesandbatches/next-sanity-image#upgrading-from-4xx-to-5xx.

surjithctly commented 1 year ago

Instead of blurDataURL={mySanityData.image.asset.metadata.lqip}, can't we return the same from {...imageProps} ?

So the code will look better. In your hook, you can just call this metadata lqip and return as blurDataURL?

lorenzodejong commented 1 year ago

@surjithctly i agree that this would make the API cleaner. Let me explain why this can't be standardised, and provide you with a way to work around this for your codebase.

Design choice

Data from Sanity can be fetched by using a query language (GROQ, GraphQL). Using this query language you can select which fields are returned from the response, in the format which works best for your solution. To clarify, i've added the following query in the README of this library:

const data = await configuredSanityClient.fetch(
  `{
    "mySanityData": *[_type == "mySanityType" && slug.current == $slug][0] {
      image {
        asset->{
          ...,
          metadata
        }
      }
    }
  }`,
  { slug }
);

However you could also choose to only query the lqip metadata from the asset at the root level:

const data = await configuredSanityClient.fetch(
  `{
    "mySanityData": *[_type == "mySanityType" && slug.current == $slug][0] {
      image {
        asset,
        "lqip": asset->metadata.lqip
      }
    }
  }`,
  { slug }
);

These two examples return different data structures. This library does not want to make any assumptions on required data structure. Besides that you can now choose to omit the blur placeholder logic altogether. Or even implement the placeholder using a BlurHash (https://www.sanity.io/docs/image-metadata#74bfd1db9b97).

Using a custom hook

If you made the design choice in your application to always return the lqip data URL in a specific way, you can create a custom hook to provide the data in the way you suggested.

import { useNextSanityImage } from 'next-sanity-image';

export function useNextSanityImageWithLQIP(sanityClient, image, options) {
    const props = useNextSanityImage(sanityClient, image, options);
    if (!props) {
        return null;
    }

    return {
        ...props,
        placeholder: 'blur',
        blurDataURL: image.asset.metadata.lqip
    }
}

When using this custom hook you can now spread {...imageProps} again.

surjithctly commented 1 year ago

Thanks, so for this hook, we have to get the metadata from the query right?

I'm thinking of some edge cases like having multiple images, so we have to do the -> many times and also if used inside portableText. In that case user might have one or many images inside.

But I guess, if sanity provided an option to return base64 from image-url plugin, this would be easier. right?

lorenzodejong commented 1 year ago

@surjithctly you can always wrap a component around it which abstracts this behavior and uses your custom hook. This works well when having multiple images (render your custom Image component for each image in an Array.map) or rendering portableText (using a custom component: https://www.sanity.io/docs/presenting-block-text#c8e78c624f83).

If Sanity would provide this from the image-url that would be easier. However that library is structured in a way where all invocations are synchronous. No data is being fetched from the actual Sanity API or CDN. The image-url library only builds up the URL structure for fetching the image. In order to retrieve the LQIP base64 data URL a roundtrip must be made to the Sanity API in order to fetch the required data. This will likely not be a supported feature anytime soon.