New Release v2.4 ⚡ Anycast POP Nodes Active • AVIF & WebP 2.0 Engine Live Try Real-Time Sandbox →
@zawcloud/images

Next.js, Nuxt and Astro

Keep using next/image, <NuxtImg> and Astro's <Image>. One setting points them at the edge, which then serves every size they ask for, in AVIF or WebP when the browser supports it.

One setting: your delivery hostname

https://a3f9c21b.zwcld.net

This is an example. Your own is on the console dashboard: an opaque <label>.zwcld.net, or a custom domain once it is verified. Sign in and the console shows this guide with your hostname filled in.

The hostname identifies the account, so no API key goes in your markup, and the URLs are safe to build in the browser. Images come from your configured origin: /photos/hero.jpg means <origin>/photos/hero.jpg.

One package, no dependencies. The framework writes the srcset; the edge resizes, converts to AVIF or WebP and caches each size on its first request. Nothing is processed at build time.

npm install @zawcloud/images

Next.js

Point next/image at a loader file:

// next.config.js
module.exports = {
  images: {
    loader: 'custom',
    loaderFile: './zawcloud-loader.js',
  },
};
// zawcloud-loader.js
import { createLoader } from '@zawcloud/images/next';

export default createLoader({ baseUrl: 'https://a3f9c21b.zwcld.net' });

Then use <Image> as usual. Next asks the loader for one URL per width in its deviceSizes and imageSizes:

import Image from 'next/image';

<Image src="/photos/hero.jpg" width={1200} height={800} sizes="100vw" alt="" />

To keep the hostname in configuration instead, set it in the environment and re-export the default loader:

# .env.local
NEXT_PUBLIC_ZAWCLOUD_URL=https://a3f9c21b.zwcld.net

// zawcloud-loader.js
export { default } from '@zawcloud/images/next';

createLoader takes any option except width and height, which Next chooses. A quality set on <Image> wins.

Nuxt Image

Register the provider in nuxt.config:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/image'],
  image: {
    provider: 'zawcloud',
    providers: {
      zawcloud: {
        provider: '@zawcloud/images/nuxt',
        options: { baseURL: 'https://a3f9c21b.zwcld.net' },
      },
    },
  },
});
<NuxtImg src="/photos/hero.jpg" width="1200" sizes="sm:100vw md:50vw" fit="cover" />

width, height, quality, format and fit map onto the edge's parameters. Nuxt's contain letterboxes, so it becomes pad and its background becomes bg; inside becomes contain and outside becomes cover. Any other modifier, for example :modifiers="{ blur: 20 }", is passed through by name. preset works as a provider option or per image.

Astro

Set it as the image service:

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  image: {
    service: {
      entrypoint: '@zawcloud/images/astro',
      config: { baseUrl: 'https://a3f9c21b.zwcld.net' },
    },
  },
});
---
import { Image } from 'astro:assets';
---
<Image src="/photos/hero.jpg" width={1200} height={800} widths={[400, 800, 1200]} alt="" />

quality takes Astro's names (low 50, mid 70, high 85, max 100) or a number. widths and densities both become a srcset. SVGs pass through untouched.

An imported image (import hero from './hero.jpg') has a built path such as /_astro/hero.a1b2c3.jpg. The edge can fetch it only if your origin is your deployed site. Otherwise use string paths on your image origin.

Anywhere else

The URL builder the integrations share works in any JavaScript, browser or server:

import { imageUrl } from '@zawcloud/images';

imageUrl('/photos/hero.jpg', { width: 800, fit: 'cover', params: { blur: 10 } }, {
  baseUrl: 'https://a3f9c21b.zwcld.net',
});
// https://a3f9c21b.zwcld.net/photos/hero.jpg?w=800&fit=cover&blur=10

Options

Option Edge parameter Notes
width, height w, h Clamped to 5000
quality q 1–100. The edge's default is 80
format f auto (the default) picks AVIF or WebP per browser and is left out of the URL. jpg means jpeg
fit fit cover, contain, fill or pad. crop means cover. pad fills the box with params.bg
preset preset One of your named presets
params as named Any other parameter: { grayscale: true, watermark: '© Shop' }

Options are written in a fixed order, so one image at one size is always the same URL, and every cache can reuse it. A value the edge would refuse, such as quality 0 or an unknown fit, throws in your build instead of becoming a broken image in production.

Two account settings to check

  • Output size limits. If your account allows only certain widths, any other width is refused with a 400. Next's deviceSizes and imageSizes, and Astro's widths, must then be widths you allow. Set them to the same lists, or leave the account unrestricted.
  • Presets-only delivery. Every URL then needs a preset. Set one in the loader or provider config.