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

# Responsive images

> Guide to generating responsive images with the ButterCMS Image API for optimal performance across all devices.

## Overview

Responsive images ensure your website delivers optimally-sized images to every device, reducing page load times and bandwidth usage. The ButterCMS Image API makes this easy through URL-based transformations.

### Why responsive images matter

* **Performance**: Smaller images load faster on mobile devices
* **Bandwidth**: Save data for users on limited connections
* **SEO**: Google rewards fast-loading pages
* **User Experience**: Images display correctly across all screen sizes

## Basic implementation

### Using srcset

The `srcset` attribute lets browsers choose the best image size:

```html theme={null}
<img
  src="https://cdn.buttercms.com/resize=width:800/abc123"
  sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
  alt="Responsive image example"
/>
```

### Understanding srcset and sizes

| Attribute | Purpose                                                  |
| --------- | -------------------------------------------------------- |
| `src`     | Fallback for browsers that don't support srcset          |
| `srcset`  | List of available image URLs with width descriptors      |
| `sizes`   | Media conditions mapping viewport widths to image widths |

## Common patterns

### Full-width hero image

```html theme={null}
<img
  src="https://cdn.buttercms.com/resize=width:1200/abc123"
  sizes="100vw"
  alt="Hero image"
/>
```

### Blog Post featured image

```html theme={null}
<img
  src="https://cdn.buttercms.com/resize=width:800/abc123"
  sizes="(max-width: 768px) 100vw, 800px"
  alt="Blog featured image"
/>
```

### Card grid image

```html theme={null}
<img
  src="https://cdn.buttercms.com/resize=width:400,height:300,fit:crop/abc123"
  sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 400px"
  alt="Card image"
/>
```

## Using the picture element

For art direction (different crops for different devices):

```html theme={null}
<picture>
  <!-- Mobile: square crop -->
  <source
    media="(max-width: 600px)"
  />
  <!-- Tablet: 16:9 crop -->
  <source
    media="(max-width: 1200px)"
  />
  <!-- Desktop: wide crop -->
  <img
    src="https://cdn.buttercms.com/resize=width:1920,height:800,fit:crop/abc123"
    alt="Art directed image"
  />
</picture>
```

## Framework examples

### React component

```jsx theme={null}
function ResponsiveImage({ src, alt, sizes = '100vw' }) {
  const fileId = src.split('/').pop();
  const baseUrl = 'https://cdn.buttercms.com';

  const widths = [400, 800, 1200, 1600];

  const srcSet = widths
    .map(w => `${baseUrl}/resize=width:${w}/${fileId} ${w}w`)
    .join(', ');

  return (
    <img
      src={`${baseUrl}/resize=width:800/${fileId}`}
      srcSet={srcSet}
      sizes={sizes}
      alt={alt}
      loading="lazy"
    />
  );
}

// Usage
<ResponsiveImage
  src="https://cdn.buttercms.com/abc123"
  alt="My image"
  sizes="(max-width: 768px) 100vw, 800px"
/>
```

### Vue component

```vue theme={null}
<template>
  <img
    :src="defaultSrc"
    :srcset="srcSet"
    :sizes="sizes"
    :alt="alt"
    loading="lazy"
  />
</template>

<script setup>
import { computed } from 'vue';

const props = defineProps({
  src: String,
  alt: String,
  sizes: { type: String, default: '100vw' },
  widths: { type: Array, default: () => [400, 800, 1200, 1600] }
});

const fileId = computed(() => props.src.split('/').pop());
const baseUrl = 'https://cdn.buttercms.com';

const defaultSrc = computed(() =>
  `${baseUrl}/resize=width:800/${fileId.value}`
);

const srcSet = computed(() =>
  props.widths
    .map(w => `${baseUrl}/resize=width:${w}/${fileId.value} ${w}w`)
    .join(', ')
);
</script>
```

### Next.js with next/image

```jsx theme={null}
import Image from 'next/image';

// Custom loader for ButterCMS
const butterLoader = ({ src, width, quality }) => {
  const fileId = src.split('/').pop();
  const q = quality || 75;
  return `https://cdn.buttercms.com/resize=width:${width}/quality=value:${q}/${fileId}`;
};

export default function ButterImage({ src, alt, ...props }) {
  return (
    <Image
      loader={butterLoader}
      src={src}
      alt={alt}
      {...props}
    />
  );
}

// Usage
<ButterImage
  src="https://cdn.buttercms.com/abc123"
  alt="My image"
  width={800}
  height={600}
  sizes="(max-width: 768px) 100vw, 800px"
/>
```

## WebP with fallback

Serve WebP to supported browsers with JPEG fallback:

```html theme={null}
<picture>
  <source
    type="image/webp"
    sizes="(max-width: 768px) 100vw, 800px"
  />
  <img
    src="https://cdn.buttercms.com/resize=width:800/abc123"
    sizes="(max-width: 768px) 100vw, 800px"
    alt="WebP with fallback"
  />
</picture>
```

## Helper functions

### JavaScript utility

```javascript theme={null}
class ButterImage {
  constructor(url) {
    this.fileId = url.split('/').pop();
    this.baseUrl = 'https://cdn.buttercms.com';
  }

  resize(width, height, fit = 'clip') {
    let params = `width:${width}`;
    if (height) params += `,height:${height}`;
    if (fit !== 'clip') params += `,fit:${fit}`;
    return `${this.baseUrl}/resize=${params}/${this.fileId}`;
  }

  generateSrcSet(widths, options = {}) {
    const { height, fit, format } = options;

    return widths.map(width => {
      let transforms = [];

      // Resize
      let resizeParams = `width:${width}`;
      if (height) {
        const scaledHeight = Math.round(height * (width / widths[widths.length - 1]));
        resizeParams += `,height:${scaledHeight}`;
      }
      if (fit) resizeParams += `,fit:${fit}`;
      transforms.push(`resize=${resizeParams}`);

      // Format conversion
      if (format) {
        transforms.push(`output=format:${format}`);
      }

      const url = `${this.baseUrl}/${transforms.join('/')}/${this.fileId}`;
      return `${url} ${width}w`;
    }).join(', ');
  }
}

// Usage
const img = new ButterImage('https://cdn.buttercms.com/abc123');

console.log(img.resize(800));
// https://cdn.buttercms.com/resize=width:800/abc123

console.log(img.generateSrcSet([400, 800, 1200], { format: 'webp' }));
// https://cdn.buttercms.com/resize=width:400/output=format:webp/abc123 400w, ...
```

## Best practices

<CardGroup cols={2}>
  <Card title="Use Appropriate Sizes" icon="ruler">
    Don't serve 4K images for thumbnails. Match image sizes to display sizes.
  </Card>

  <Card title="Lazy Loading" icon="hourglass">
    Add `loading="lazy"` for images below the fold.
  </Card>

  <Card title="WebP When Possible" icon="image">
    WebP offers 25-35% smaller files than JPEG at similar quality.
  </Card>

  <Card title="Test Performance" icon="gauge">
    Use Lighthouse or WebPageTest to verify image optimization.
  </Card>
</CardGroup>

## Recommended breakpoints

| Device Type   | Typical Width | Suggested srcset widths |
| ------------- | ------------- | ----------------------- |
| Mobile        | 320-480px     | 400w, 600w              |
| Tablet        | 768-1024px    | 800w, 1000w             |
| Desktop       | 1200-1920px   | 1200w, 1600w, 1920w     |
| Large Display | 2560px+       | 2400w                   |

## Next steps

<CardGroup cols={2}>
  <Card title="Image Transformations" icon="wand-magic-sparkles" href="../image-api/image-transformations">
    All transformation options
  </Card>

  <Card title="Security" icon="shield" href="../image-api/security-authentication">
    Secure your images
  </Card>

  <Card title="Performance" icon="bolt" href="/build-your-app/performance/cdn-global-delivery">
    CDN optimization
  </Card>

  <Card title="Media Field" icon="photo-film" href="/getting-started/field-types-validation/media-field">
    Working with media fields
  </Card>
</CardGroup>
