image icon indicating copy to clipboard operation
image copied to clipboard

[New Provider] Add support for Umbraco ImageSharp as a Nuxt Image provider

Open DMOShareIT opened this issue 5 months ago • 1 comments

Hi Nuxt team šŸ‘‹

I'm currently working on a project using Umbraco CMS as a headless backend, and I’m leveraging its built-in image processing capabilities powered by Umbraco CMS (ImageSharp.Web).

It would be extremely useful to have native support for Umbraco as a provider in @nuxt/image. Umbraco allows image transformations via query parameters, which makes it very similar in behavior to other dynamic image providers.

Example URLs from Umbraco ImageSharp

/media/image.jpg?width=600
/media/image.jpg?width=600&height=300&rmode=crop&rxy=0.5,0.2
/media/image.jpg?format=webp&quality=80
/media/image.jpg?bgcolor=128,64,32&rmode=pad

Thanks for the fantastic module — it has been a huge help optimizing images in Nuxt apps.

Best regards,

DMOShareIT avatar Jul 10 '25 11:07 DMOShareIT

For now I tried to create a custom provider, but it would be better to have this oficially supported, so far I've this, still needs some testing. I read about a NUXT_IMAGE_PROVIDER on the documentation but can anyone give me an example of what value should be there? Is the name of the provider? So if I put there umbraco I dont need to always set provider="umbraco" everytime?

Nuxt Config:

image: {
    providers: {
      umbraco: {
        provider: '~/providers/umbraco.ts',
        options: {
          baseURL: env.imageProviderUrl,
        },
      },
    },
  },

umbracoProvider:

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

/**
 * Key map is responsible for mapping readable "properties", which can be passed
 * as modifiers of the `NuxtImg` component, to query parameters that can be
 * interpreted by Umbraco's API.
 */
const keyMap = {
  width: 'width',
  height: 'height',
  format: 'format',
  quality: 'quality',
  rmode: 'rmode',
  rsampler: 'rsampler',
  ranchor: 'ranchor',
  rxy: 'rxy',
  orient: 'orient',
  compand: 'compand',
  bgcolor: 'bgcolor',
  token: 'token', // For HMAC
} as const;

/**
 * Value map is responsible for mapping readable modifier values defined in
 * `keyMap`, as well as native modifiers of the `NuxtImg` component, to URL query
 * values that can be interpreted by Umbraco's API.
 *
 * ```Examples
 * Umbraco's `width` query param expects an arbitrary number, so it does not need to be in `valueMap`.
 *
 * Our custom param `width` maps directly to `width` in keyMap, so it does not need a valueMap entry either.
 *
 * However, Umbraco's `format` param only supports `jpg`, `png`, `gif`, `pbm`, `tga`, `tiff`, `webp`.
 *
 * So if the user passes `<NuxtImg :modifiers="{ format: 'jpeg' }" />`,
 * we must map `jpeg` → `jpg` since Umbraco does not recognize `jpeg`.
 * Same thing for `tif` → `tiff`
 *
 * Similarly, if a user passes `fit: 'cover'`, we translate it to `fit: 'crop'`
 * because that's the terminology Umbraco uses, defaulting to `centre`.
 * ```
 */
const valueMap = {
  format: {
    jpeg: 'jpg',
    tif: 'tiff',
  },
  ranchor: {
    bottom: 'Bottom',
    'bottom-left': 'BottomLeft',
    'bottom-right': 'BottomRight',
    center: 'Center',
    left: 'Left',
    right: 'Right',
    top: 'Top',
    'top-left': 'TopLeft',
    'top-right': 'TopRight',
  },
  rmode: {
    cover: 'Crop',
    contain: 'Pad',
    stretch: 'Stretch',
    fill: 'BoxPad',
    inside: 'Max',
    outside: 'Min',
    manual: 'Manual',
  },
  rsampler: {
    bicubic: 'Bicubic',
    nearest: 'NearestNeighbor',
    box: 'Box',
    mitchell: 'MitchellNetravali',
    catmull: 'CatmullRom',
    lanczos2: 'Lanczos2',
    lanczos3: 'Lanczos3',
    lanczos5: 'Lanczos5',
    lanczos8: 'Lanczos8',
    welch: 'Welch',
    robidoux: 'Robidoux',
    robidouxsharp: 'RobidouxSharp',
    spline: 'Spline',
    triangle: 'Triangle',
    hermite: 'Hermite',
  },
} as const;

/**
 * The formatter defines how individual key/value pairs are turned into query parameters.
 * For example: `width=800`, or `format=jpg`.
 * Boolean `true` modifiers are treated as flags, e.g., `auto=true` → `auto`.
 */
export function formatter(key: string, value: string | number | boolean): string {
  return String(value) === 'true' ? key : `${key}=${value}`;
}

const operationsGenerator = createOperationsGenerator({
  formatter,
  joinWith: '&', // standard query parameters
  keyMap,
  valueMap,
});

/**
 * Provider for Umbraco-compatible image URLs, used by Nuxt Image module.
 *
 * It builds the final image URL with any modifiers passed via the `NuxtImg` component.
 *
 * Example:
 * ```vue
 * <NuxtImg
 *   provider="umbraco"
 *   src="/media/example.jpg"
 *   :modifiers="{ width: 600, format: 'jpeg' }"
 * />
 * ```
 * → Output:
 * `/media/example.jpg?width=600&format=jpg`
 */
export const getImage: ProviderGetImage = (src, { modifiers = {}, baseUrl } = {}) => {
  if (!baseUrl) {
    baseUrl = '';
  }

  const operations = operationsGenerator(modifiers);

  return {
    url: joinURL(baseUrl, src + (operations ? `?${operations}` : '')),
  };
};

DMOShareIT avatar Jul 11 '25 09:07 DMOShareIT