Use `<NuxtImg>` with served blob image
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 <NuxtImg> provider that uses Cloudflare Images in production and falls back to the original R2 image in development.
nuxt.config.ts
nuxt.config.ts image: {
providers: {
cloudflareOnProd: {
provider: '~/providers/cloudflareOnProd.ts',
options: {
prodSiteURL: 'https://example.com'
}
}
}
}
cloudflareOnProd.ts
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
cloudflareOnProd.tsimport { 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
}
}
Thank you for pointing me in the direction of custom image provider for handling blobs @lorenzofiamingo Would be nice if this was initially handled by nuxtImg.
I'm not sure if this is the best way to do this. But I managed to use NuxtImg without creating a custom provider. Basically, you need to add route rules for the blob server route and @nuxt/image alias. This works on deployed projects on Cloudflare too.
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/blob/**': { ssr: false },
},
image: {
domains: ['localhost:3000'],
alias: {
images: 'http://localhost:3000/blob',
},
},
})
// server/routes/blob/[...pathname].get.ts
export default eventHandler(async (event) => {
const { pathname } = getRouterParams(event)
if (!pathname) {
return createError({
statusCode: 404,
statusMessage: 'Not Found',
})
}
setHeader(event, 'Content-Security-Policy', 'default-src \'none\';')
return hubBlob().serve(event, pathname)
})
Thanks @larrasu and @lorenzofiamingo!
I guess this is something that will be fixed by #668 ?