Skip to content

Use <NuxtImg> with served blob image #521

@lorenzofiamingo

Description

@lorenzofiamingo

Is your feature request related to a problem? Please describe.
I’m trying to display an image served from my NuxtHub blob, as explained here. While it works perfectly with a standard tag, the image doesn’t show up when I use <NuxtImg>.
The default ipx provider of <NuxtImg> doesn't work since the image is returned from an api path.
The none provider works fine like <img> but the image is returned as is.
The cloudflare provider works but only in production, when the website served by cloudflare. (In deployed development could work but with any origin activated and specifying the production url as the base url of the provider)

Describe the solution you'd like
Would be nice to have a nuxthub provider (or maybe a <NuxtHubImg>) that permits to leverage <NuxtImg> out-of-the-box.

Describe alternatives you've considered
For now since Cloudflare Images doesn’t provide a way to set images when working locally, I created a provider that uses Cloudflare Images in production and falls back to the original R2 image in development.

nuxt.config.ts

image: {
  providers: {
    cloudflareOnProd: {
      provider: '~/providers/cloudflareOnProd.ts',
      options: {
        prodSiteURL: 'https://example.com'
      }
    }
  }
}

cloudflareOnProd.ts

import type { ProviderGetImage } from '@nuxt/image'
import { getImage as getImageWithCloudflare } from '#image/providers/cloudflare'
import { getImage as getImageWithNone } from '#image/providers/none'

export const getImage: ProviderGetImage = (src, options, ctx) => {
    if (useRuntimeConfig().public.siteURL === options.prodSiteURL) {
        return getImageWithCloudflare(src, options, ctx)
    } else {
        return getImageWithNone(src, options, ctx)
    }
}
Legacy

cloudflareOnProd.ts

import { joinURL, encodeQueryItem } from 'ufo'
import type { ProviderGetImage } from '@nuxt/image'
import { createOperationsGenerator } from '#image'

const operationsGenerator = createOperationsGenerator({
    keyMap: {
        width: 'w',
        height: 'h',
        dpr: 'dpr',
        fit: 'fit',
        gravity: 'g',
        quality: 'q',
        format: 'f',
        sharpen: 'sharpen',
    },
    valueMap: {
        fit: {
            cover: 'cover',
            contain: 'contain',
            fill: 'scale-down',
            outside: 'crop',
            inside: 'pad',
        },
        gravity: {
            auto: 'auto',
            side: 'side',
        },
    },
    joinWith: ',',
    formatter: (key, val) => encodeQueryItem(key, val),
})

const defaultModifiers = {}

// https://developers.cloudflare.com/images/image-resizing/url-format/
export const getImage: ProviderGetImage = (src, {
    modifiers = {},
    baseURL = '/',
    prodSiteURL
} = {}) => {
    const mergeModifiers = { ...defaultModifiers, ...modifiers }
    const operations = operationsGenerator(mergeModifiers as any)
    
    let url
    
    if (useRuntimeConfig().public.siteURL === prodSiteURL) {
        // https://<ZONE>/cdn-cgi/image/<OPTIONS>/<SOURCE-IMAGE>
        url = operations ? joinURL(baseURL, 'cdn-cgi/image', operations, src) : joinURL(baseURL, src)
    } else {
        url = joinURL(baseURL, src)
    }
    
    return {
        url
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions