> ## Documentation Index
> Fetch the complete documentation index at: https://docs.convertly.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Responsive images & art direction

> When to use ConvertlyImage, srcset, and the HTML picture element for resolution switching and breakpoint-specific crops.

Responsive images usually solve one of two problems: **resolution switching** (same composition, different pixel widths) or **art direction** (different crop, aspect ratio, or focal point per viewport). Convertly handles both from a single source file — you change URL parameters, not maintain separate exports.

<Note>
  **Prerequisites:** a [CDN endpoint](/guides/image-cdn-setup) and either Convertly Storage file IDs or a configured [origin source](/guides/image-cdn-setup). Install `@convertly-sh/image` before using the React examples below.
</Note>

## Resolution switching vs art direction

| Goal                                                  | Best approach                                      | Convertly tool                                         |
| ----------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------ |
| Same image, pick the right file size for the viewport | `srcset` + `sizes`                                 | `<ConvertlyImage>` or `cdn.srcset()`                   |
| Different crop or aspect ratio at each breakpoint     | `<picture>` with `<source media="…">`              | `createConvertlyCdn()` + manual `<picture>` markup     |
| Subject moves or reframes on mobile vs desktop        | `<picture>` + different `w`, `h`, `fit`, `gravity` | URL builder per `<source>`                             |
| Next.js app, minimal markup changes                   | `next/image` custom loader                         | [`createConvertlyLoader`](/docs/image-cdn/sdks#nextjs) |

**Rule of thumb:** if only the pixel width changes, use `<ConvertlyImage>`. If the *composition* changes, use `<picture>`.

<Frame caption="One source image — four `<picture>` breakpoints with different aspect ratios and `fit=cover` crops.">
  <img src="https://cdn.convertly.sh/cvly/o/convertly-site/docs/cdn-transforms/examples/responsive-images/picture-breakpoints.webp?v=docs-20260717&format=auto&q=auto:eco" alt="Art-directed picture element crops at desktop, tablet, phablet, and mobile breakpoints" className="w-full rounded-xl" />
</Frame>

Resize your browser or use DevTools device mode to see how `<picture>` picks the matching `<source>`. Chrome may fetch twice in the inspector — that is a tooling quirk, not double-loading in production.

## `<ConvertlyCdnProvider>` — configure once

`<ConvertlyCdnProvider>` is a React context wrapper that creates **one shared CDN client** (`createConvertlyCdn`) for every `<ConvertlyImage>` and `<ConvertlyVideo>` underneath it.

```tsx theme={"system"}
import { ConvertlyCdnProvider, ConvertlyImage } from "@convertly-sh/image/react";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <ConvertlyCdnProvider
      namespace={process.env.NEXT_PUBLIC_CONVERTLY_CDN_NAMESPACE!}
      baseUrl="https://cdn.convertly.sh" // optional; default shown
    >
      {children}
    </ConvertlyCdnProvider>
  );
}
```

**What it stores**

| Prop               | Purpose                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `namespace`        | Your CDN endpoint's public URL namespace (e.g. `assets-acme`). Required unless you pass `namespace` on every image.            |
| `baseUrl`          | CDN host. Defaults to `https://cdn.convertly.sh`. Set to your [custom domain](/docs/image-cdn/custom-domain) when live.        |
| `includeNamespace` | Whether URLs include `/{endpointNamespace}/` in the path. Auto-detected from `baseUrl`; override only if you know you need to. |

**Why use it**

* **DRY config** — set `namespace` once in your root layout instead of on every image.
* **Single CDN instance** — avoids recreating the URL builder on every render.
* **Composable** — child components call `<ConvertlyImage src="…" />` without knowing env vars.

**When you can skip it**

Pass `namespace` directly on `<ConvertlyImage>`, or use the framework-agnostic `createConvertlyCdn()` in Astro, Svelte, or plain HTML. Vue uses `provideConvertlyCdn()` for the same pattern; Solid uses `<ConvertlyCdnProvider>` identically to React.

<Warning>
  Use `NEXT_PUBLIC_CONVERTLY_CDN_NAMESPACE` (your CDN endpoint's public URL namespace), never a secret `cvly_…` API key. Namespace values are safe in client bundles; API keys are not.
</Warning>

## Resolution switching with `<ConvertlyImage>`

`<ConvertlyImage>` renders a real `<img>` with Convertly CDN URLs. When you pass `width` and `sizes`, it automatically builds a responsive `srcSet` using [`defaultWidths()`](/docs/image-cdn/sdks#defaultwidths) and sets `format=auto` + `gravity=auto` by default.

<Frame caption="Same composition, different widths — the browser picks a `srcset` candidate (320w left, 1200w right).">
  <img src="https://cdn.convertly.sh/cvly/o/convertly-site/docs/cdn-transforms/examples/responsive-images/srcset-widths.webp?v=docs-20260717&format=auto&q=auto:eco" alt="320 pixel wide vs 1200 pixel wide responsive srcset candidates side by side" className="w-full rounded-xl" />
</Frame>

```tsx theme={"system"}
<ConvertlyImage
  src="hero-summer"
  width={1200}
  height={800}
  fit="cover"
  alt="Summer collection hero"
  sizes="(min-width: 1280px) 1200px, (min-width: 768px) 80vw, 100vw"
/>
```

That produces markup equivalent to:

```html theme={"system"}
<img
  src="https://cdn.convertly.sh/assets-acme/hero-summer?w=1200&h=800&fit=cover&format=auto&gravity=auto"
  sizes="(min-width: 1280px) 1200px, (min-width: 768px) 80vw, 100vw"
  width="1200"
  height="800"
  loading="lazy"
  decoding="async"
  alt="Summer collection hero"
/>
```

### Origin-backed images

For assets on a registered origin source (e.g. deployed `public/` folder):

```tsx theme={"system"}
<ConvertlyImage
  origin="site"
  src="/hero.jpg"
  width={1200}
  sizes="100vw"
  alt="Homepage hero"
/>
```

### Custom width lists

Override the default breakpoint ladder when your layout needs specific steps:

```tsx theme={"system"}
<ConvertlyImage
  src="hero-summer"
  width={800}
  widths={[400, 600, 800, 1200, 1600]}
  sizes="(min-width: 768px) 50vw, 100vw"
  alt="Product card"
/>
```

### Next.js: global loader instead of per-component markup

If you use `next/image` everywhere, wire [`createConvertlyLoader`](/docs/image-cdn/sdks#nextjs) once — every `<Image>` gets CDN URLs without switching to `<ConvertlyImage>`. Use `<ConvertlyImage>` when you need transform props (`preset`, text overlays, `gravity=face`) that `next/image` does not pass through.

## Art direction with `<picture>`

Use `<picture>` when the **crop or aspect ratio** should change at a breakpoint — for example a wide landscape hero on desktop and a tall portrait crop on mobile.

Convertly does not ship a `<ConvertlyPicture>` component today. Build `<picture>` with the URL builder; each `<source>` gets its own transform params.

### Plain HTML

Replace `{endpointNamespace}` and `{fileIdOrSlug}` with your values:

```html theme={"system"}
<picture>
  <source
    media="(min-width: 1280px)"
  />
  <source
    media="(min-width: 768px)"
  />
  <source
    media="(min-width: 480px)"
  />
  <img
    src="https://cdn.convertly.sh/{endpointNamespace}/{fileIdOrSlug}?w=320&h=568&fit=cover&gravity=face&format=auto"
    alt="Campaign hero"
    loading="lazy"
    decoding="async"
  />
</picture>
```

One origin image. Four CDN URLs. Each variant is generated on demand and cached at the edge.

<Frame caption="Source landscape (top) — the same file reframed at each breakpoint below.">
  <img src="https://cdn.convertly.sh/cvly/o/convertly-site/docs/cdn-transforms/examples/original.webp?v=docs-20260717&format=auto&q=auto:eco" alt="Wide hero source image before art-directed crops" className="w-full rounded-xl mb-4" />

  <img src="https://cdn.convertly.sh/cvly/o/convertly-site/docs/cdn-transforms/examples/responsive-images/picture-breakpoints.webp?v=docs-20260717&format=auto&q=auto:eco" alt="Four art-directed crops for picture element breakpoints" className="w-full rounded-xl" />
</Frame>

### React with the URL builder

```tsx theme={"system"}
import { createConvertlyCdn } from "@convertly-sh/image";

const cdn = createConvertlyCdn({
  namespace: process.env.NEXT_PUBLIC_CONVERTLY_CDN_NAMESPACE!,
});

const fileId = "hero-summer";

export function HeroPicture() {
  return (
    <picture>
      <source
        media="(min-width: 1280px)"
        srcSet={cdn.url(fileId, { w: 1280, h: 720, fit: "cover", gravity: "smart" })}
      />
      <source
        media="(min-width: 768px)"
        srcSet={cdn.url(fileId, { w: 768, h: 1024, fit: "cover", gravity: "face" })}
      />
      <img
        src={cdn.url(fileId, { w: 320, h: 568, fit: "cover", gravity: "face" })}
        alt="Campaign hero"
        loading="lazy"
        decoding="async"
      />
    </picture>
  );
}
```

If you already use `<ConvertlyCdnProvider>`, call `useConvertlyCdn()` inside child components instead of creating a second client:

```tsx theme={"system"}
import { useConvertlyCdn } from "@convertly-sh/image/react";

function HeroPicture() {
  const cdn = useConvertlyCdn();
  // … same <picture> markup as above
}
```

### Adding `srcset` per breakpoint

For art-directed sources that also need multiple widths within a breakpoint, combine `cdn.srcset()` on each `<source>`:

```tsx theme={"system"}
<picture>
  <source
    media="(min-width: 768px)"
    srcSet={cdn.srcset(fileId, {
      w: 768,
      h: 1024,
      fit: "cover",
      gravity: "face",
      widths: [640, 768, 1024, 1280],
    })}
    sizes="80vw"
  />
  <img
    src={cdn.url(fileId, { w: 480, h: 640, fit: "cover", gravity: "face" })}
    srcSet={cdn.srcset(fileId, {
      w: 480,
      h: 640,
      fit: "cover",
      gravity: "face",
      widths: [320, 480, 640],
    })}
    sizes="100vw"
    alt="Campaign hero"
  />
</picture>
```

## Automated cropping for art direction

When the subject is not centred, pick a gravity mode per breakpoint instead of pre-cropping assets:

| Mode              | Use when                                                                             |
| ----------------- | ------------------------------------------------------------------------------------ |
| `gravity=auto`    | Default saliency crop — good baseline                                                |
| `gravity=smart`   | Off-centre subjects, busy scenes                                                     |
| `gravity=face`    | Portraits and team photos                                                            |
| `gravity=entropy` | Textures and detail-heavy frames                                                     |
| `fp=x,y`          | You know the focal point (from CMS or [analyze API](/docs/image-cdn/smart-cropping)) |

<Frame caption="Portrait square crop — `gravity=center` (left) vs `gravity=face` (right) on a group photo.">
  <img src="https://cdn.convertly.sh/cvly/o/convertly-site/docs/cdn-transforms/examples/responsive-images/face-portrait-crops.webp?v=docs-20260717&format=auto&q=auto:eco" alt="Center crop vs face-aware crop on a team photo" className="w-full rounded-xl" />
</Frame>

```html theme={"system"}
<!-- Portrait crop that locks onto the nearest face -->
<img src="https://cdn.convertly.sh/{endpointNamespace}/{fileIdOrSlug}?w=400&h=400&fit=cover&gravity=face" alt="Team headshot" />
```

See [Smart cropping & image analysis](/docs/image-cdn/smart-cropping) for focal points, the analyze endpoint, and before/after examples.

## Retina and high-DPI screens

Convertly uses **width descriptors** in `srcset` (`320w`, `640w`, …). The browser picks a URL whose width matches the rendered size × device pixel ratio. You do not need separate `1x` / `2x` density descriptors — include widths up to \~2× your layout width (which `defaultWidths()` does automatically).

**Alternative:** fixed layout width + `dpr=2` on the URL. See [Device pixel ratio](/docs/image-cdn/transforms/resize-crop-fit#device-pixel-ratio) and [Client Hints](/guides/client-hints) when the slot width is fluid.

Example: an image displayed at `600px` CSS width on a 2× screen may request the `1200w` candidate from the set.

<Frame caption="Width descriptors cover retina — a 600px layout slot on a 2× display typically selects the 1200w URL from the generated set.">
  <img src="https://cdn.convertly.sh/cvly/o/convertly-site/docs/cdn-transforms/examples/responsive-images/srcset-widths.webp?v=docs-20260717&format=auto&q=auto:eco" alt="Small and large srcset width candidates for retina displays" className="w-full rounded-xl" />
</Frame>

## Quick reference

| Component / API           | Renders                     | Best for                                              |
| ------------------------- | --------------------------- | ----------------------------------------------------- |
| `<ConvertlyCdnProvider>`  | (context only)              | Shared `namespace` + CDN client for React/Solid trees |
| `<ConvertlyImage>`        | `<img>` + optional `srcset` | Resolution switching, lazy loading, transform props   |
| `createConvertlyCdn()`    | URL strings                 | Astro, Svelte, `<picture>`, non-React stacks          |
| `createConvertlyLoader()` | Next.js loader              | Existing `next/image` markup                          |
| `<picture>` + URL builder | `<picture>`                 | Art direction across breakpoints                      |

## Related docs

* [Framework SDKs](/docs/image-cdn/sdks) — full prop tables and Vue/Svelte/Solid adapters
* [Image CDN setup](/guides/image-cdn-setup) — origin sources, namespaces, first deploy
* [Smart cropping](/docs/image-cdn/smart-cropping) — focal points and analyze API
* [Resize, crop & fit](/docs/image-cdn/transforms/resize-crop-fit) — `fit`, `gravity`, and crop examples
