---
title: "Image"
description: "Images with aspect ratio, object fit and parallax."
requested_language: nl
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/nl/components/image
version: unreleased (main)
docs_index: https://docs.systhema.app/nl/llms.txt
---
> This page isn't translated yet. Showing English.


`Image` renders a responsive image cropped to one of the Systhema aspect ratios, with the media border, radius and shadow from the tokens. It can also move against the scroll as a parallax layer. In `@systhemaui/next` it wraps `next/image`. [`Video`](https://docs.systhema.app/nl/components/video.md) and [`MediaWrapper`](https://docs.systhema.app/nl/components/media-wrapper.md) share the same media props.

```tsx preview iframe height=420 title="Image"
import { Image } from '@systhemaui/next'

export default function Demo() {
  return (
    <div className="w-full max-w-3xl">
      <Image
        src="/demo-assets/mountains.webp"
        alt="Pastel mountain ridges under a pale sky"
        width={1920}
        height={1080}
        aspectRatio="16/9"
      />
    </div>
  )
}
```

## Import

```tsx
import { Image } from '@systhemaui/next'
```

In a React app without Next.js, import it from `@systhemaui/react`. There it renders a plain `<img>` with `loading="lazy"` and `decoding="async"`.

## Examples

### Aspect ratio

`aspectRatio` crops the image to a fixed ratio with an `aspect-*` class. It defaults to `'auto'`, the image's own ratio. The full list runs from `32/9` to `9/21`, plus the golden ratios `1.618/1` and `1/1.618`.

```tsx preview iframe height=360 title="Aspect ratios"
import { Image, Paragraph } from '@systhemaui/next'

const ratios = ['1/1', '4/3', '16/9', '21/9'] as const

export default function Demo() {
  return (
    <div className="grid w-full max-w-3xl grid-cols-2 gap-6 md:grid-cols-4">
      {ratios.map((ratio) => (
        <div key={ratio} className="flex flex-col gap-2">
          <Image
            src="/demo-assets/coast.webp"
            alt=""
            width={1800}
            height={1200}
            aspectRatio={ratio}
          />
          <Paragraph type="small">{ratio}</Paragraph>
        </div>
      ))}
    </div>
  )
}
```

`21/9-md` is the one responsive value: 4/3 on small screens and 21/9 from the `md` breakpoint up. Switch the preview between phone and desktop to see it change.

```tsx preview iframe height=420 title="Responsive 21/9-md"
import { Image } from '@systhemaui/next'

export default function Demo() {
  return (
    <div className="w-full max-w-3xl">
      <Image
        src="/demo-assets/city.webp"
        alt="A city skyline at night"
        width={1920}
        height={1080}
        aspectRatio="21/9-md"
      />
    </div>
  )
}
```

### Object fit

`objectFit` decides how the image fills its box: `'cover'` (the default) crops to fill it, `'contain'` shows the whole image and leaves space around it. Here a portrait image sits in square boxes.

```tsx preview iframe height=360 title="cover and contain"
import { Image, Paragraph } from '@systhemaui/next'

export default function Demo() {
  return (
    <div className="grid w-full max-w-xl grid-cols-2 gap-6">
      <div className="flex flex-col gap-2">
        <Image
          src="/demo-assets/dunes-portrait.webp"
          alt=""
          width={1200}
          height={1600}
          aspectRatio="1/1"
          objectFit="cover"
        />
        <Paragraph type="small">cover</Paragraph>
      </div>
      <div className="flex flex-col gap-2">
        <Image
          src="/demo-assets/dunes-portrait.webp"
          alt=""
          width={1200}
          height={1600}
          aspectRatio="1/1"
          objectFit="contain"
        />
        <Paragraph type="small">contain</Paragraph>
      </div>
    </div>
  )
}
```

### Object position

With `cover`, `objectPosition` picks which part of the image stays in the crop. It takes a keyword (`'top'`, `'bottom-left'`, `'center'` and so on, default `'center'`) or an `{ x, y }` pair, which is the shape the Payload image fields store.

```tsx preview iframe height=320 title="Object position"
import { Image, Paragraph } from '@systhemaui/next'

export default function Demo() {
  return (
    <div className="grid w-full max-w-3xl grid-cols-3 gap-6">
      <div className="flex flex-col gap-2">
        <Image src="/demo-assets/forest-portrait.webp" alt="" width={1200} height={1600} aspectRatio="16/9" objectPosition="top" />
        <Paragraph type="small">top</Paragraph>
      </div>
      <div className="flex flex-col gap-2">
        <Image src="/demo-assets/forest-portrait.webp" alt="" width={1200} height={1600} aspectRatio="16/9" />
        <Paragraph type="small">center</Paragraph>
      </div>
      <div className="flex flex-col gap-2">
        <Image
          src="/demo-assets/forest-portrait.webp"
          alt=""
          width={1200}
          height={1600}
          aspectRatio="16/9"
          objectPosition={{ x: 'center', y: 'bottom' }}
        />
        <Paragraph type="small">{'{ x: center, y: bottom }'}</Paragraph>
      </div>
    </div>
  )
}
```

### Parallax

`parallax` wraps the image in a `<figure class="media-wrapper parallax-wrapper">` that carries the aspect ratio, and adds the `parallax` class to the image. The [`ParallaxListener`](https://docs.systhema.app/nl/components/listeners.md#parallaxlistener) then moves the image inside the wrapper as the page scrolls. With `parallax`, `className` and the scroll-reveal classes go on the wrapper.

```tsx preview iframe height=420 title="Parallax"
import { Image } from '@systhemaui/next'

export default function Demo() {
  return (
    <div className="w-full max-w-3xl">
      <Image
        src="/demo-assets/mountains.webp"
        alt="Pastel mountain ridges under a pale sky"
        width={1920}
        height={1080}
        aspectRatio="21/9"
        parallax
      />
    </div>
  )
}
```

The preview sizes itself to its content, so it does not scroll and the image stays at its resting position. On a page, the image moves as it passes through the viewport. Under `prefers-reduced-motion: reduce` it stays still. The travel range and scale are set with the `parallax` config (see [Parallax](https://docs.systhema.app/nl/styling/parallax.md)).

`parallaxNoWrapper` adds the `parallax` class without the wrapper, for an image you place in your own `overflow: hidden` container.

## Props

`Image` takes every `<img>` attribute (in Next.js, every `next/image` prop) on top of the media props below. `width` and `height` are not set on the element while `parallax` is on, since the wrapper decides the size. `disableAnimation` drops the configured `animationClasses`.

<!-- generated:props @systhemaui/react ImageProps -->

| Prop                        | Type                                                                                                                                                                                                                                              | Default | Description |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------- |
| `className`                 | `string`                                                                                                                                                                                                                                          | -       |             |
| `disableAnimation`          | `boolean`                                                                                                                                                                                                                                         | -       |             |
| `aspectRatio`               | `AspectRatio` (`auto`, `32/9`, `21/9`, `21/9-md`, `14/3`, `5/2`, `3/1`, `4/1`, `2/1`, `16/5`, `8/3`, `16/9`, `16/10`, `8/5`, `5/4`, `4/3`, `3/2`, `1.618/1`, `1/1`, `9/8`, `2/3`, `3/4`, `4/5`, `6/7`, `9/16`, `10/16`, `1/2`, `9/21`, `1/1.618`) | -       |             |
| `parallax`                  | `boolean`                                                                                                                                                                                                                                         | -       |             |
| `parallaxNoWrapper`         | `boolean`                                                                                                                                                                                                                                         | -       |             |
| `objectFit`                 | `'contain' \| 'cover' \| null`                                                                                                                                                                                                                    | -       |             |
| `objectPosition`            | `'top-left' \| 'top' \| 'top-center' \| 'top-right' \| 'center-left' \| 'left' \| 'center-center' \| 'center' \| 'center-right' \| 'right' \| 'bottom-left' \| 'bottom' \| 'bottom-center' \| 'bottom-right' \| { x?, y? } \| null`               | -       |             |
| …and all `<img>` attributes |                                                                                                                                                                                                                                                   |         |             |

<!-- /generated -->

## HTML and CSS

Without React, combine `media` with an aspect-ratio and an object-fit class:

```html
<img
  class="media aspect-16/9 object-cover object-center"
  src="/mountains.webp"
  alt="Pastel mountain ridges under a pale sky"
  width="1920"
  height="1080"
  loading="lazy"
  decoding="async"
/>

<figure class="media-wrapper parallax-wrapper aspect-21/9">
  <img class="media parallax object-cover" src="/mountains.webp" alt="" />
</figure>
```

The aspect classes are listed on [Aspect ratio](https://docs.systhema.app/nl/styling/aspect-ratio.md), and the parallax classes on [Parallax](https://docs.systhema.app/nl/styling/parallax.md). Without React, `initParallax()` from the [vanilla JS bundle](https://docs.systhema.app/nl/styling/vanilla-js.md) runs the parallax engine.

<!-- generated:utilities media -->

| Class                | Styles                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `media`              | `.hero-simple-media:is(.media) { max-width: unset; left: 50%; translate: -50% 0; width: var(--hero-simple-media-width, 100%); }`<br>`.hero-background-media:is(.media) { z-index: 5; position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; border-radius: 0px; box-shadow: none; }`<br>`.hero-background-media:is(.media)  > * { width: 100%; height: 100%; border-radius: 0px; box-shadow: none; }`<br>+ 11 more rules                         |
| `media-wrapper`      | `.hero-simple-media:is(.media-wrapper) { max-width: unset; left: 50%; translate: -50% 0; width: var(--hero-simple-media-width, 100%); }`<br>`.hero-background-media:is(.media-wrapper) { z-index: 5; position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; border-radius: 0px; box-shadow: none; }`<br>`.hero-background-media:is(.media-wrapper)  > * { width: 100%; height: 100%; border-radius: 0px; box-shadow: none; }`<br>+ 12 more rules |
| `media-overlay`      | `& { z-index: 1; position: absolute; inset: 0; opacity: 1; pointer-events: none; transition-property: opacity; transition-duration: 500ms; transition-timing-function: ease-out; color: var(--color-media-icon); }`<br>`.media:has(video) .media-overlay { pointer-events: auto; }`<br>`.media-wrapper:has(video) .media-overlay { pointer-events: auto; }`<br>`[data-playing="true"] .media-overlay { opacity: 0; pointer-events: none; }`<br>+ 7 more rules                                  |
| `media-overlay-icon` | `& { position: absolute; top: 50%; left: 50%; translate: -50% -50%; color: var(--color-media-icon); box-shadow: inset 0 0 0 100vmax var(--color-media-overlay), 0 0 0 100vmax var(--color-media-overlay); width: var(--media-icon-size); height: var(--media-icon-size); font-size: var(--media-icon-size); line-height: var(--media-icon-size); }`<br>+ 1 more rule                                                                                                                           |

<!-- /generated -->

## Next.js

The `@systhemaui/next` `Image` renders `next/image`, so images are resized, served in modern formats and lazy-loaded. It takes `next/image` props instead of `<img>` props and adds:

- The media props above: `aspectRatio`, `objectFit`, `objectPosition`, `parallax` and `parallaxNoWrapper`.
- `sizes` defaults to `'100vw'`. Pass the real display width (`sizes="(min-width: 768px) 50vw, 100vw"`) for images narrower than the viewport, so the browser picks a smaller file.
- `.gif` and `.svg` sources skip the optimizer automatically. Set `unoptimized` yourself only for other formats that must bypass it.
- `fill` is turned off while `parallax` is on.
- `loading` defaults to `'lazy'`, and is dropped when you pass `priority`, because `next/image` rejects both together. Pass `priority` for the largest image above the fold.

```tsx
import { Image } from '@systhemaui/next'

export function HeroImage() {
  return (
    <Image
      src="/images/hero.webp"
      alt="Our studio in Ghent"
      width={1920}
      height={1080}
      aspectRatio="21/9"
      sizes="100vw"
      priority
    />
  )
}
```

Pass `width` and `height` (the source's intrinsic size, which sets the aspect ratio before the file loads) or `fill`.

<!-- generated:props @systhemaui/next ImageProps -->

| Prop                               | Type                                                                                                                                                                                                                                              | Default | Description |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------- |
| `className`                        | `string`                                                                                                                                                                                                                                          | -       |             |
| `disableAnimation`                 | `boolean`                                                                                                                                                                                                                                         | -       |             |
| `aspectRatio`                      | `AspectRatio` (`auto`, `32/9`, `21/9`, `21/9-md`, `14/3`, `5/2`, `3/1`, `4/1`, `2/1`, `16/5`, `8/3`, `16/9`, `16/10`, `8/5`, `5/4`, `4/3`, `3/2`, `1.618/1`, `1/1`, `9/8`, `2/3`, `3/4`, `4/5`, `6/7`, `9/16`, `10/16`, `1/2`, `9/21`, `1/1.618`) | -       |             |
| `parallax`                         | `boolean`                                                                                                                                                                                                                                         | -       |             |
| `parallaxNoWrapper`                | `boolean`                                                                                                                                                                                                                                         | -       |             |
| `objectFit`                        | `'contain' \| 'cover'`                                                                                                                                                                                                                            | -       |             |
| `objectPosition`                   | `'top-left' \| 'top' \| 'top-center' \| 'top-right' \| 'center-left' \| 'left' \| 'center-center' \| 'center' \| 'center-right' \| 'right' \| 'bottom-left' \| 'bottom' \| 'bottom-center' \| 'bottom-right' \| { x?, y? } \| null`               | -       |             |
| …and props from `next`             |                                                                                                                                                                                                                                                   |         |             |
| …and all inherited HTML attributes |                                                                                                                                                                                                                                                   |         |             |

<!-- /generated -->

## Accessibility

- Describe what the image shows in `alt`. Leave `alt` empty (`alt=""`, the default) for a decorative image or one whose content is already in the text next to it.
- Parallax motion stops under `prefers-reduced-motion: reduce`, and the image rests filling its wrapper.

See [Reduced motion](https://docs.systhema.app/nl/concepts/accessibility.md#reduced-motion).

## Related

- [Figure](https://docs.systhema.app/nl/components/figure.md)
- [MediaWrapper](https://docs.systhema.app/nl/components/media-wrapper.md)
- [Image block](https://docs.systhema.app/nl/payload/blocks/image.md)
- [Aspect ratio](https://docs.systhema.app/nl/styling/aspect-ratio.md)
- [Parallax](https://docs.systhema.app/nl/styling/parallax.md)
