nuxt / image

Plug-and-play image optimization for Nuxt applications.
https://image.nuxt.com
MIT License
1.34k stars 270 forks source link

Nuxt Image sizes attribute doesn't work with Strapi provider #641

Open maximelebreton opened 1 year ago

maximelebreton commented 1 year ago

Hello!

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

nuxt.configs.js ```javascript //... 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!

aufdenpunkt commented 1 year ago

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

highoncarbs commented 1 year ago

any update on this ?

PeritonM commented 1 year ago

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.

highoncarbs commented 1 year ago

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

jiblett1000 commented 1 year ago

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 commented 1 year ago

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!

ulysse-lacour commented 1 year ago

@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...

jiblett1000 commented 1 year ago

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.

ulysse-lacour commented 1 year ago

@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.

jiblett1000 commented 1 year ago

@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 commented 1 year ago

@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.

ulysse-lacour commented 1 year ago

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 :)

jiblett1000 commented 1 year ago

@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/

ulysse-lacour commented 1 year ago

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

style0092 commented 1 year ago

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!!!

Krislunde commented 1 year ago

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.

alexMugen commented 11 months ago

Hello,

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

Thanks

sparrow-chik-chrk commented 4 months ago

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>