---
title: Image
related:
  - title: Spinners
    path: spinners.md
  - title: Quasar Components Transitions
    path: ../options/transitions.md
---
The QImg component makes working with images (any picture format) easy and also adds a nice loading effect to it along with many other features (example: the ability to set an aspect ratio).

## QImg API

### Props

- `ratio` (string | number, optional)
  Force the component to maintain an aspect ratio
  Examples:
    - `1`
    - `'1.7778'`
    - `:ratio="4/3"`
    - `:ratio="16/9"`
    - `(Number format) :ratio="16/9"`
    - `(String format) ratio="1"`
- `src` (string, optional)
  Path to image
  Examples: `(public folder) src="img/something.png"`, `(assets folder) src="~@/assets/my-img.gif"`, `(relative path format) :src="require('./my_img.jpg')"`, `(URL) src="https://picsum.photos/500/300"`
- `srcset` (string, optional)
  Same syntax as <img> srcset attribute
  Examples:
    - `'elva-fairy-320w.jpg 320w, elva-fairy-480w.jpg 480w'`
- `sizes` (string, optional)
  Same syntax as <img> sizes attribute
  Examples:
    - `'(max-width: 320px) 280px, (max-width: 480px) 440px, 800px'`
- `placeholder-src` (string, optional)
  While waiting for your image to load, you can use a placeholder image
  Examples: `(public folder) placeholder-src="img/some-placeholder.png"`, `(assets folder) placeholder-src="~@/assets/my-placeholder.gif"`, `(relative path format) :placeholder-src="require('./placeholder.jpg')"`, `(URL) placeholder-src="https://picsum.photos/500/300"`
- `error-src` (string, optional) *(added v2.15)*
  In case your image fails to load, you can use an error image
  Examples: `(public folder) error-src="img/some-placeholder.png"`, `(assets folder) error-src="~@/assets/my-placeholder.gif"`, `(relative path format) :error-src="require('./placeholder.jpg')"`, `(URL) error-src="https://picsum.photos/500/300"`
- `initial-ratio` (string | number, optional), default `1.7778`
  Use it when not specifying 'ratio' but still wanting an initial aspect ratio
  Examples: `(Number format) :initial-ratio="16/9"`, `(String format) initial-ratio="1"`
- `width` (string, optional)
  Forces image width; Must also include the unit (px or %)
  Examples: `'280px'`, `'70%'`
- `height` (string, optional)
  Forces image height; Must also include the unit (px or %)
  Examples: `'280px'`, `'70%'`
- `loading` (string, optional), default `'lazy'`
  Lazy or immediate load; Same syntax as <img> loading attribute
  Accepts: `'lazy'`, `'eager'`
- `loading-show-delay` (number | string, optional), default `0` *(added v2.14.6)*
  Delay showing the spinner when image changes; Gives time for the browser to load the image from cache to prevent flashing the spinner unnecessarily; Value should represent milliseconds
  Examples: `500`, `'700'`
- `crossorigin` (string, optional)
  Same syntax as <img> crossorigin attribute
  Accepts: `'anonymous'`, `'use-credentials'`
- `decoding` (string, optional)
  Same syntax as <img> decoding attribute
  Accepts: `'sync'`, `'async'`, `'auto'`
- `referrerpolicy` (string, optional)
  Same syntax as <img> referrerpolicy attribute
  Accepts: `'no-referrer'`, `'no-referrer-when-downgrade'`, `'origin'`, `'origin-when-cross-origin'`, `'same-origin'`, `'strict-origin'`, `'strict-origin-when-cross-origin'`, `'unsafe-url'`
- `fetchpriority` (string, optional), default `'auto'`
  Provides a hint of the relative priority to use when fetching the image
  Accepts: `'high'`, `'low'`, `'auto'`
- `fit` (string, optional), default `'cover'`
  How the image will fit into the container; Equivalent of the object-fit prop; Can be coordinated with 'position' prop
  Accepts: `'cover'`, `'fill'`, `'contain'`, `'none'`, `'scale-down'`
- `position` (string, optional), default `'50% 50%'`
  The alignment of the image into the container; Equivalent of the object-position CSS prop
  Examples: `'0 0'`, `'20px 50px'`
- `alt` (string, optional)
  Specifies an alternate text for the image, if the image cannot be displayed
  Examples: `'Two cats'`
- `draggable` (boolean, optional)
  Adds the native 'draggable' attribute
- `img-class` (string, optional)
  CSS classes to be attributed to the native img element
  Examples: `'my-special-class'`
- `img-style` (object, optional)
  Apply CSS to the native img element
  Examples: `{ transform: 'rotate(45deg)' }`
- `spinner-color` (string, optional)
  Color name for default Spinner (unless using a 'loading' slot)
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `spinner-size` (string, optional)
  Size in CSS units, including unit name, for default Spinner (unless using a 'loading' slot)
  Examples: `'16px'`, `'2rem'`
- `no-spinner` (boolean, optional)
  Do not display the default spinner while waiting for the image to be loaded; It is overriden by the 'loading' slot when one is present
- `no-native-menu` (boolean, optional)
  Disables the native context menu for the image
- `no-transition` (boolean, optional)
  Disable default transition when switching between old and new image
- `ssr-prerender` (boolean, optional) *(added v2.32)*
  By default the image is rendered in the server-side (SSR/SSG) HTML; When none of 'ratio', 'initial-ratio' or 'height' is specified the shape of the box cannot be known until the image loads, so the image is rendered client-side after hydration instead; Set this prop to still render it server-side, accepting that the box changes to the natural ratio of the image once hydrated

### Events

- `@load`
  Emitted when image has been loaded by the browser
  Params:
    - `src` (string, optional)
      URL of image that has been loaded; useful when using 'srcset' and/or 'sizes'
      Examples: `'https://some-site.net/some-img.gif'`
- `@error`
  Emitted when browser could not load the image
  Params:
    - `evt` (Event, optional)
      JS Event object (same as the browser's native 'error' event)

### Slots

- `#default`
  Default slot can be used for captions. See examples
- `#loading`
  While image is loading, this slot is being displayed on top of the component; Suggestions: a spinner or text
- `#error`
  Optional slot to be used when image could not be loaded; make sure you assign a min-height and min-width to the component through CSS

## Usage

### Basic

```vue
<template>
  <div class="q-gutter-sm">
    <q-btn push color="teal" label="Change image" @click="refresh" />

    <q-img
      :src="url"
      spinner-color="white"
      style="height: 140px; max-width: 150px"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const url = ref('https://picsum.photos/500/300')

function refresh() {
  url.value = 'https://picsum.photos/500/300?t=' + Math.random()
}
</script>
```

### Aspect ratio

Example "Custom aspect ratio":

```vue
<template>
  <div class="q-col-gutter-md row items-start">
    <div class="col-4">
      Ratio: 16/9
      <q-img src="https://picsum.photos/500/300" :ratio="16 / 9" />
    </div>

    <div class="col-4">
      Ratio: 1
      <q-img src="https://picsum.photos/500/300" :ratio="1" />
    </div>

    <div class="col-4">
      Ratio: 4/3
      <q-img src="https://picsum.photos/500/300" :ratio="4 / 3" />
    </div>
  </div>
</template>
```

### Captions

```vue
<template>
  <div class="q-col-gutter-md row items-start">
    <div class="col-6">
      <q-img src="https://cdn.quasar.dev/img/parallax2.jpg">
        <div class="absolute-bottom text-subtitle1 text-center">
          Caption
        </div>
      </q-img>
    </div>

    <div class="col-6">
      <q-img src="https://cdn.quasar.dev/img/parallax2.jpg">
        <div class="absolute-top text-center"> Caption </div>
      </q-img>
    </div>

    <div class="col-6">
      <q-img src="https://cdn.quasar.dev/img/parallax2.jpg">
        <div class="absolute-bottom-right text-subtitle2"> Caption </div>
      </q-img>
    </div>

    <div class="col-6">
      <q-img src="https://cdn.quasar.dev/img/parallax2.jpg">
        <div class="absolute-full text-subtitle2 flex flex-center">
          Caption
        </div>
      </q-img>
    </div>
  </div>
</template>
```

### Image style

In the example below, we add a blur and sepia effect. Furthermore, we make use of the `rounded-borders` CSS helper class.

Example "Custom image style":

```vue
<template>
  <q-img
    src="https://cdn.quasar.dev/img/parallax2.jpg"
    spinner-color="white"
    style="height: 170px; max-width: 300px"
    img-class="my-custom-image"
    class="rounded-borders"
  >
    <div class="absolute-bottom text-subtitle1 text-center"> Caption </div>
  </q-img>
</template>

<style lang="sass">
.my-custom-image
  filter: blur(1px) sepia()
</style>
```

### Fit mode

There are multiple ways in which the image can be displayed through the `fit` property: 'cover', 'fill' (default), 'contain', 'none', 'scale-down'. It is basically the same thing as the CSS prop called [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit).

Some modes lead to empty space (horizontally or vertically) besides the image.

You can also configure the position through `position` property, which is equivalent to the CSS [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) one. Its default value is "50% 50%".

Example "Fit modes":

```vue
<template>
  <div class="q-gutter-md row items-start">
    <q-img
      v-for="mode in fitModes"
      :key="mode"
      src="https://picsum.photos/500/300"
      style="max-width: 300px; height: 150px"
      :fit="mode"
    >
      <div class="absolute-bottom text-subtitle1 text-center">
        {{ mode }}
      </div>
    </q-img>
  </div>
</template>

<script setup>
const fitModes = ['cover', 'fill', 'contain', 'none', 'scale-down']
</script>
```

### Loading states

Example "Loading state":

```vue
<template>
  <q-btn
    push
    color="teal"
    label="Change image"
    @click="refresh"
    class="q-mb-md"
  />

  <div class="q-gutter-sm row items-start">
    <q-img
      :src="url"
      spinner-color="red"
      style="height: 140px; max-width: 150px"
    />

    <q-img
      :src="url"
      spinner-color="primary"
      spinner-size="82px"
      style="height: 140px; max-width: 150px"
    />

    <q-img :src="url" style="height: 140px; max-width: 150px">
      <template #loading>
        <div class="text-subtitle1 text-white"> Loading... </div>
      </template>
    </q-img>

    <q-img :src="url" style="height: 140px; max-width: 150px">
      <template #loading>
        <q-spinner-gears color="white" />
      </template>
    </q-img>

    <q-img :src="url" style="height: 140px; max-width: 150px">
      <template #loading>
        <div class="text-yellow">
          <q-spinner-ios />
          <div class="q-mt-md">Loading...</div>
        </div>
      </template>
    </q-img>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const url = ref('https://picsum.photos/500/300')

function refresh() {
  url.value = 'https://picsum.photos/500/300?t=' + Math.random()
}
</script>
```

When you have big-sized images, you can use a placeholder image (recommended to be specified in base64 encoding) like in the example below. The placeholder will be displayed until the target image gets loaded. We're toggling the QImg tag so you can see the placeholder image in action.

Example "Placeholder source":

```vue
<template>
  <div class="q-gutter-md">
    <q-btn push color="teal" label="Toggle" @click="toggle" />

    <!-- using v-if so you can see the effect -->
    <q-img
      v-if="url !== null"
      :src="url"
      :ratio="1"
      class="q-mt-md"
      style="width: 150px"
      placeholder-src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAACWBAMAAADOL2zRAAAAG1BMVEXMzMyWlpaqqqq3t7fFxcW+vr6xsbGjo6OcnJyLKnDGAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABAElEQVRoge3SMW+DMBiE4YsxJqMJtHOTITPeOsLQnaodGImEUMZEkZhRUqn92f0MaTubtfeMh/QGHANEREREREREREREtIJJ0xbH299kp8l8FaGtLdTQ19HjofxZlJ0m1+eBKZcikd9PWtXC5DoDotRO04B9YOvFIXmXLy2jEbiqE6Df7DTleA5socLqvEFVxtJyrpZFWz/pHM2CVte0lS8g2eDe6prOyqPglhzROL+Xye4tmT4WvRcQ2/m81p+/rdguOi8Hc5L/8Qk4vhZzy08DduGt9eVQyP2qoTM1zi0/uf4hvBWf5c77e69Gf798y08L7j0RERERERERERH9P99ZpSVRivB/rgAAAABJRU5ErkJggg=="
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const url = ref(null)

function toggle() {
  url.value =
    url.value === null
      ? 'https://picsum.photos/500/300?t=' + Math.random()
      : null
}
</script>
```

Example "Error state":

```vue
<template>
  <div class="q-gutter-sm">
    <q-img
      src="https://cdn.quasar.dev/img/non-existent-image-src.png"
      style="height: 140px; max-width: 150px"
    >
      <template #error>
        <div class="absolute-full flex flex-center bg-negative text-white">
          Cannot load image
        </div>
      </template>
    </q-img>

    <q-img
      src="https://cdn.quasar.dev/img/non-existent-image-src.png"
      error-src="https://cdn.quasar.dev/logo-v2/header.png"
      style="height: 140px; max-width: 150px"
    >
      <template #error>
        <div class="absolute-full flex flex-center"> Error encountered </div>
      </template>
    </q-img>
  </div>
</template>
```

### Responsive

> [!NOTE]
> To grasp the `sizes` and `srcset` properties, please read about native support on [responsive images](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#Why_responsive_images) because **QImg relies on that entirely**.

```vue
<template>
  <div class="q-gutter-md row items-start">
    <q-img
      src="https://cdn.quasar.dev/img/image-src.png"
      srcset="https://cdn.quasar.dev/img/image-1x.png 300w,
              https://cdn.quasar.dev/img/image-2x.png 2x,
              https://cdn.quasar.dev/img/image-3x.png 3x,
              https://cdn.quasar.dev/img/image-4x.png 4x"
      style="height: 280px; max-width: 300px"
    >
      <div class="absolute-bottom text-body1 text-center"> With srcset </div>
    </q-img>

    <q-img
      src="https://cdn.quasar.dev/img/image-src.png"
      srcset="https://cdn.quasar.dev/img/image-1x.png 400w,
              https://cdn.quasar.dev/img/image-2x.png 800w,
              https://cdn.quasar.dev/img/image-3x.png 1200w,
              https://cdn.quasar.dev/img/image-4x.png 1600w"
      sizes="(max-width: 400px) 400w,
            (min-width: 400px) and (max-width: 800px) 800w,
            (min-width: 800px) and (max-width: 1200px) 1200w,
            (min-width: 1200px) 1600w"
      style="height: 280px; max-width: 300px"
    >
      <div class="absolute-bottom text-body1 text-center">
        With srcset & sizes
      </div>
    </q-img>
  </div>
</template>
```

> [!NOTE]
> For `sizes` property, please read about Resolution Switching: [Different Sizes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#Resolution_switching_Different_sizes).

> [!NOTE]
> For `srcset` property, please read about Resolution Switching: [Same size, different resolutions](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#Resolution_switching_Same_size_different_resolutions).

### No native context menu

In the example below we disable the native context menu on the images.

> [!IMPORTANT]
> When you are using this option always take care to have the content of the `default` or `error` slots wrapped in a `div` element, or add a `all-pointer-events` class on the element.

Example "Native context menu":

```vue
<template>
  <div class="q-col-gutter-md row items-start">
    <div class="col-6">
      <q-img src="https://cdn.quasar.dev/img/parallax2.jpg" no-native-menu>
        <div class="absolute-bottom text-subtitle1 text-center">
          Caption
        </div>
      </q-img>
    </div>

    <div class="col-6">
      <q-img src="https://cdn.quasar.dev/img/parallax2.jpg" no-native-menu>
        <div class="absolute-top text-center"> Caption </div>
      </q-img>
    </div>

    <div class="col-6">
      <q-img src="https://cdn.quasar.dev/img/parallax2.jpg" no-native-menu>
        <div class="absolute-bottom-right text-subtitle2"> Caption </div>
      </q-img>
    </div>

    <div class="col-6">
      <q-img src="https://cdn.quasar.dev/img/parallax2.jpg" no-native-menu>
        <q-icon
          class="absolute all-pointer-events"
          size="32px"
          name="info"
          color="white"
          style="top: 8px; left: 8px"
        >
          <q-tooltip> Tooltip </q-tooltip>
        </q-icon>
      </q-img>
    </div>
  </div>
</template>
```

## Server-side rendering *(v2.32+)*

On SSR/SSG, QImg puts the `<img>` into the server HTML whenever you have declared the shape of its box, meaning when any of `ratio`, `initial-ratio` or `height` is set. The browser then discovers and fetches the image while parsing the HTML, long before hydration, and paints it as it arrives, like a native `<img>`. The loading state (spinner or `loading` slot) only shows up if the image is still loading once the page gets hydrated, and the `load`/`error` events are emitted at hydration for an image that has already settled.

Without any of them, the box uses a default 16:9 ratio until the natural ratio of the image is known, so a server-rendered image would visibly change shape at hydration. QImg therefore defers such an image until hydration, unless you set `ssr-prerender` and accept that box change.

> [!TIP]
> For an above-the-fold image, also set `loading="eager"` and `fetchpriority="high"` so that the browser does not delay its fetch.

## Accessibility *(v2.25+)*

The QImg wrapper carries `role="img"` with its accessible name taken from the `alt` prop — provide `alt` for any image that carries meaning. For a purely decorative image set `alt=""`, exactly as you would on a native `<img>`: the wrapper then claims no role at all (the img role requires a name) and screen readers skip it. Omitting `alt` entirely does the same thing. Note that the loading and error state changes are not announced to assistive technology.
