image icon indicating copy to clipboard operation
image copied to clipboard

Nuxt Image sizes attribute doesn't work with Strapi provider

Open maximelebreton opened this issue 3 years ago • 18 comments

Hello!

I'm trying to using Nuxt Image with Strapi provider but sizes doesn't work as excepted.

nuxt.configs.js
//...
  image: {
    strapi: {
      baseURL: "http://localhost:1337/uploads",
    },
  }

Because when I use this code:

<nuxt-img
    provider="strapi"
    loading="lazy"
    :src="`${data.file.url}`"
    sizes="sm:100vw md:100vw lg:100vw"
/>

the output is:

<img 
    src="http://localhost:1337/uploads/20190131_170142_8085401332.jpg" loading="lazy" 
    sizes="(max-width: 640px) 100vw, (max-width: 768px) 100vw,  (max-width: 1024px) 100vw" 
    srcset="http://localhost:1337/uploads/20190131_170142_8085401332.jpg 640w,
    http://localhost:1337/uploads/20190131_170142_8085401332.jpg 768w,
    http://localhost:1337/uploads/20190131_170142_8085401332.jpg 1024w"
/>

As you can see, every srcset value has the same url...!

And here is the file data coming from Strapi:

{
  "url": "/20190131_170142_8085401332.jpg",
  "height": 1440,
  "width": 1440,
  "formats": {
    "thumbnail": {
      "url": "/thumbnail_20190131_170142_8085401332.jpg"
    },
    "medium": {
      "url": "/medium_20190131_170142_8085401332.jpg"
    },
    "small": {
      "url": "/small_20190131_170142_8085401332.jpg"
    },
    "large": {
      "url": "/large_20190131_170142_8085401332.jpg"
    }
  }
}

So the excepted output should be:

<img 
    src="http://localhost:1337/uploads/20190131_170142_8085401332.jpg" loading="lazy" 
    sizes="(max-width: 640px) 100vw, (max-width: 768px) 100vw, (max-width: 1024px) 100vw" 
    srcset="http://localhost:1337/uploads/small_20190131_170142_8085401332.jpg 640w,
    http://localhost:1337/uploads/medium_20190131_170142_8085401332.jpg 768w,
    http://localhost:1337/uploads/large_20190131_170142_8085401332.jpg 1024w"
/>

But when I look into the Strapi Provider: (https://github.com/nuxt/image/blob/v1/src/runtime/providers/strapi.ts), I can't see anything about these formats...

So how can I put these small, medium and large urls into srcset?

Am I missing something, or is it just not implemented?

Version "@nuxt/image-edge": "^1.0.0-27769790.4b27db3", "nuxt": "3.0.0-rc.11"

Thanks!

maximelebreton avatar Nov 08 '22 21:11 maximelebreton

I have the same case, and it would be awesome if there were a solution 🙂

aufdenpunkt avatar Dec 16 '22 12:12 aufdenpunkt

any update on this ?

highoncarbs avatar Mar 04 '23 12:03 highoncarbs

Yeah this is super annoying. The integration of Nuxt with Strapi in general doesn't seem very well documented at all.

Hope there will be an update for this soon.

PeritonM avatar Mar 07 '23 21:03 PeritonM

@PeritonM I switched to cloudinary, that seems to be working and for local images add provider="static"

highoncarbs avatar Mar 09 '23 09:03 highoncarbs

Hey ya'll,

So after looking into the code what I can surmise is that this is not currently possible and is not technically a bug in the module. What is happening is that by default, Strapi does not provide url parameters to request custom images. However, the module is using the sizes in the prop to request images using said url parameters and then set the srcset attribute accordingly. I added a console.log to the module locally to see what the srcset came out to. This is what it was:

srcset
: 
"http://0.0.0.0:1337/uploads/image.jpg 128w, http://0.0.0.0:1337/uploads/image.jpg 320w"

As you can see, since Strapi doesn't support query parameters or anything like that, nuxt-image is just using the same image url everytime. I'm considering working on a PR that incorporates this strapi plugin:

https://www.npmjs.com/package/strapi-plugin-local-image-sharp

That should theoretically enable the ability to use sizes with strapi after mapping the parameters to the plugin.. However since this a 3rd party plugin, it doesn't seem ideal. It is also the only foreseeable solution until Strapi includes this functionality by default.

I would love to hear from the nuxt team on this. I would be happy to work on a PR for this functionality.

jiblett1000 avatar Mar 10 '23 00:03 jiblett1000

In the event the above PR doesn't get accepted, I'm posting instructions here for implementing the "local image sharp" plugin as a custom provider in Strapi.

  1. Install the local image sharp Strapi plugin. https://market.strapi.io/plugins/strapi-plugin-local-image-sharp

  2. Create the file ~/providers/localImageSharp.ts and inside put:

import { joinURL } from "ufo";
import type { ProviderGetImage } from "@nuxt/image-edge";
import { createOperationsGenerator } from "#image";

export const getImage: ProviderGetImage = (
  src,
  { modifiers, baseURL = "http://localhost:1337/uploads" } = {}
) => {
  const operationsGenerator = createOperationsGenerator({
    keyMap: {
      width: "width",
      height: "height",
      resize: "resize",
      fit: "fit",
      position: "positon",
      trim: "trim",
      format: "format",
      quality: "quality",
      rotate: "rotate",
      enlarge: "enlarge",
      flip: "flip",
      flop: "flop",
      sharpen: "sharpen",
      median: "median",
      gamma: "gamma",
      negate: "negate",
      normalize: "normalize",
      threshold: "threshold",
      grayscale: "grayscale",
      animated: "animated",
    },
    joinWith: ",",
    formatter: (key: string, value: string) => `${key}_${value}`,
  });

  const operations = operationsGenerator(modifiers as any);

  return {
    url: joinURL(baseURL, operations, src),
  };
};

  1. Add this in your nuxt.config.ts:
image: {
    providers: {
      localImageSharp: {
        provider: "~/providers/localImageSharp",
        options: {
          baseURL: `${process.env.STRAPI_URL}/uploads/`,
        },
      },
    },
    provider: 'localImageSharp'
  },
  1. Sit back and enjoy sizes working properly.

*Note: The median modifier doesn't seem to be working. I've raised an issue in the local image sharp plugin repo.

Also, if you have any ideas on how to improve upon the above code, feel free to chime in of course.

Cheers!

jiblett1000 avatar Mar 11 '23 21:03 jiblett1000

@jiblett1000 Pardon me but what is this file #image in 'import { createOperationsGenerator } from "#image";' ?

Your solution seems perfect but I can't manage to make it work...

ulysse-lacour avatar Aug 09 '23 07:08 ulysse-lacour

Hello @ulysse-lacour . #image is a directory alias added by the nuxt/image module. Ctrl + clicking on it should bring you to the index file of the image module.

jiblett1000 avatar Aug 09 '23 08:08 jiblett1000

@jiblett1000 Thank you very much, didn't realized I needed both nuxt/image and nuxt/image-edge (if I'm not mistaking)! Set up for provider works like a charm thanks to your instructions, I have a silly question but I think a few beginners like me would really appreciate to make sure they set up cleanly : how can I make sure the sizes are finally working ?

Using this :

<nuxt-img
    loading="lazy"
    :src="`${post.attributes?.Image?.data?.attributes?.url}`"
    sizes="sm:100vw md:100vw lg:100vw"
/>

Render this img tag :

<img src="http://localhost:1337/uploads/width_1024/uploads/SQUARE_IMAGE_e2ad4450a0.png" onerror="this.setAttribute('data-error', 1)" loading="lazy" data-nuxt-img="" sizes="(max-width: 640px) 100vw, (max-width: 768px) 100vw, 100vw" srcset="http://localhost:1337/uploads/width_640/uploads/SQUARE_IMAGE_e2ad4450a0.png 640w, http://localhost:1337/uploads/width_768/uploads/SQUARE_IMAGE_e2ad4450a0.png 768w, http://localhost:1337/uploads/width_1024/uploads/SQUARE_IMAGE_e2ad4450a0.png 1024w">

So it seems to work but weirdly my files in Strapi are named like this small_SQUARE_IMAGE_e2ad4450a0.png/thumbnail_SQUARE_IMAGE_e2ad4450a0 and not with that folder architecture with width in this example.

Hopefully I'm not the ones confused by the names differences and how Strapi is handling images.

ulysse-lacour avatar Aug 10 '23 06:08 ulysse-lacour

@ulysse-lacour

You do not need to install both versions. You can use the rc release of the @nuxt/image package.

You can see if it's working by changing the "size" for one of the screen breakpoints and seeing the resulting change in file size / resolution of the requested image. For example, create a fullscreen image and then change the image size in the sizes prop from say 100vw to 20vw and you should see how the image quality is degraded and the file size is less because it's now requesting an image for only 20% of the screen width.

The addition of "width" or whatever modifiers you're using via the nuxt image custom provider above are added to the url by nuxt/image and utilized on the backend (strapi) by the local image sharp plugin to return the correctly sized image.

jiblett1000 avatar Aug 10 '23 10:08 jiblett1000

@ulysse-lacour You might also consider forming your src strings like this:

:src="${image.data.attributes.hash}${image.data.attributes.ext}"

Depending on how your base url is configured in nuxt/image, this might be necessary so it doesn't add an additional uploads into the url.

At least I seem to recall having an issue like that before. It's been a bit since I was working on this.

jiblett1000 avatar Aug 10 '23 10:08 jiblett1000

Thank a lot @jiblett1000 you're an absolute chef, it was indeed working, just had to play with sizes attribute to realize it! If you have some ressources in mind to learn more on Nuxt x Strapi integration feel free to share they would be really useful to me :)

ulysse-lacour avatar Aug 10 '23 12:08 ulysse-lacour

@ulysse-lacour No worries. Not sure I can really point you towards any particular resources except for the nuxt/strapi module (if you're not already using it). https://strapi.nuxtjs.org/

jiblett1000 avatar Aug 11 '23 03:08 jiblett1000

@jiblett1000 Thanks again, I've seen it but couldn't see any use cases for this module...

ulysse-lacour avatar Aug 11 '23 05:08 ulysse-lacour

In the event the above PR doesn't get accepted, I'm posting instructions here for implementing the "local image sharp" plugin as a custom provider in Strapi.

  1. Install the local image sharp Strapi plugin. https://market.strapi.io/plugins/strapi-plugin-local-image-sharp
  2. Create the file ~/providers/localImageSharp.ts and inside put:
import { joinURL } from "ufo";
import type { ProviderGetImage } from "@nuxt/image-edge";
import { createOperationsGenerator } from "#image";

export const getImage: ProviderGetImage = (
  src,
  { modifiers, baseURL = "http://localhost:1337/uploads" } = {}
) => {
  const operationsGenerator = createOperationsGenerator({
    keyMap: {
      width: "width",
      height: "height",
      resize: "resize",
      fit: "fit",
      position: "positon",
      trim: "trim",
      format: "format",
      quality: "quality",
      rotate: "rotate",
      enlarge: "enlarge",
      flip: "flip",
      flop: "flop",
      sharpen: "sharpen",
      median: "median",
      gamma: "gamma",
      negate: "negate",
      normalize: "normalize",
      threshold: "threshold",
      grayscale: "grayscale",
      animated: "animated",
    },
    joinWith: ",",
    formatter: (key: string, value: string) => `${key}_${value}`,
  });

  const operations = operationsGenerator(modifiers as any);

  return {
    url: joinURL(baseURL, operations, src),
  };
};
  1. Add this in your nuxt.config.ts:
image: {
    providers: {
      localImageSharp: {
        provider: "~/providers/localImageSharp",
        options: {
          baseURL: `${process.env.STRAPI_URL}/uploads/`,
        },
      },
    },
    provider: 'localImageSharp'
  },
  1. Sit back and enjoy sizes working properly.

*Note: The median modifier doesn't seem to be working. I've raised an issue in the local image sharp plugin repo.

Also, if you have any ideas on how to improve upon the above code, feel free to chime in of course.

Cheers!

thx!!!

style0092 avatar Aug 26 '23 15:08 style0092

You're a hero @jiblett1000. I've been wanting to use nuxt-image for ages, but couldn't quite figure out rolling my own provider with Strapi. Have been rolling my own image component using strapi local image sharp instead.

Krislunde avatar Oct 22 '23 00:10 Krislunde

Hello,

is it the same thing for format props ? It seems that it doesn't work with provider strapi.

Thanks

ghost avatar Nov 14 '23 15:11 ghost

This provider is intended for those who do not want dynamic image generation and are using the strapi-image-optimizer plugin to create images with multiple breakpoints and formats.

import { withBase, withoutLeadingSlash } from "ufo";
import type { ProviderGetImage } from "@nuxt/image";

// https://strapi.io/documentation/developer-docs/latest/development/plugins/upload.html#upload

export const getImage: ProviderGetImage = (src, { modifiers, baseURL } = {}) => {
    const { breakpoints, width, format } = modifiers || {};
    let chosenBreakpoint = null;

    if (width && breakpoints) {
        for (const [key, value] of Object.entries(breakpoints)) {
            const bpValue = Number(value);
            if (bpValue >= width + 50) {
                chosenBreakpoint = { key, value: bpValue };
                break;
            }
        }
    }
    const breakpointKey = chosenBreakpoint ? chosenBreakpoint.key : "";
    if (breakpointKey === "") {
        return {
            url: withBase(src, baseURL),
        };
    }
    const ext = src.split(".").pop();
    const formattedSrc = `${breakpointKey}_${format}_${withoutLeadingSlash(src).replace(`.${ext}`, "")}.${format}`;
    return {
        url: withBase(formattedSrc, baseURL),
    };
};

export const validateDomains = true;

Usage Example

<NuxtPicture
  :modifiers="{ breakpoints: { small: '500', medium: '750', large: '1280', full: '1920' } }"
  densities="x1"
  provider="strapiV2"
  sizes="200 xl:1200"
  src="background_affisha_f0db2ea4d1.png"
/>

Details src: We pass the hash and extension from Strapi. sizes: Specify the actual sizes on the layout (I implemented it with numbers only, without px). modifiers: Pass our breakpoints in the format { breakpoints: { ... } }. And don't forget to specify the required formats via format or in global settings.

Result You will have automatically generated images like this:

<picture data-v-02281a80="">
  <source
    type="image/avif"
    sizes="(max-width: 1439.9px) 200px, 1200px"
    srcset="https://your-strapi-url/uploads/small_avif_background_affisha_f0db2ea4d1.avif 200w, https://your-strapi-url/uploads/large_avif_background_affisha_f0db2ea4d1.avif 1200w"
  />
  <source
    type="image/webp"
    sizes="(max-width: 1439.9px) 200px, 1200px"
    srcset="https://your-strapi-url/uploads/small_webp_background_affisha_f0db2ea4d1.webp 200w, https://your-strapi-url/uploads/large_webp_background_affisha_f0db2ea4d1.webp 1200w"
  />
  <img
    data-nuxt-pic=""
    src="https://your-strapi-url/uploads/large_png_background_affisha_f0db2ea4d1.png"
    sizes="(max-width: 1439.9px) 200px, 1200px"
    srcset="https://your-strapi-url/uploads/small_png_background_affisha_f0db2ea4d1.png 200w, https://your-strapi-url/uploads/large_png_background_affisha_f0db2ea4d1.png 1200w"
  />
</picture>

sparrow-chik-chrk avatar Jul 01 '24 18:07 sparrow-chik-chrk