Next.js plugin for embedding optimized images.

  • import png/jpg images
  • output to webp format
  • resize to multiple screen sizes and densities
  • optimize webp and fallback images using sharp
  • lazy load in modern browsers with prop forwarding (loading="lazy")
  • prevent layout shift with automatic width/height attributes
  • streamlined usage with the built in <Picture /> component
  • art direction with different images for different breakpoints
  • fast deployment and development workflow using persistent cache

One size per breakpoint

The most typical usage. Provide one image per each device size you're targeting. For example, if you're targeting small/large at 768px breakpoint (as configured in your next.config.js), provide 2 sizes. If you're targeting mobile/tablet/desktop with breakpoints 480px and 768px (as configured in your next.config.js) - provide 3 sizes, and so on.

<Picture src={require('../images/coffee1.jpg?sizes=375,860')} />

Output

<picture>
    <source
        type="image/webp"
        srcSet="/next-img/_next/static/images/coffee1-375@1x-d04d19c08afad1ce95e7f102961c1a7e.webp 375w,
                /next-img/_next/static/images/coffee1-375@2x-9d17025a66d743b5ba03e9046e9f4365.webp 750w,
                /next-img/_next/static/images/coffee1-860@1x-b811b4e74128a685ea8029bcde250d74.webp 860w,
                /next-img/_next/static/images/coffee1-860@2x-71e3c8575537ad7bdcacee55fa91268c.webp 1720w"
        sizes="(max-width: 768px) 375px, 860px"
    />
    <source
        type="image/jpeg"
        srcSet="/next-img/_next/static/images/coffee1-375@1x-e2b9b524b9126743aa9764cfd425fa4e.jpg 375w,
                /next-img/_next/static/images/coffee1-375@2x-0a1620261148881f98ebdc78ed4ec085.jpg 750w,
                /next-img/_next/static/images/coffee1-860@1x-da2a8aa62cc17fe9e7ca51461981387c.jpg 860w,
                /next-img/_next/static/images/coffee1-860@2x-11753d1994d0b6d94afa566e9b3cd4ea.jpg 1720w"
        sizes="(max-width: 768px) 375px, 860px"
    />

    <img
        src="/next-img/_next/static/images/coffee1-375@1x-e2b9b524b9126743aa9764cfd425fa4e.jpg"
        srcSet="/next-img/_next/static/images/coffee1-375@1x-e2b9b524b9126743aa9764cfd425fa4e.jpg 375w,
                /next-img/_next/static/images/coffee1-375@2x-0a1620261148881f98ebdc78ed4ec085.jpg 750w,
                /next-img/_next/static/images/coffee1-860@1x-da2a8aa62cc17fe9e7ca51461981387c.jpg 860w,
                /next-img/_next/static/images/coffee1-860@2x-11753d1994d0b6d94afa566e9b3cd4ea.jpg 1720w"
        width="375"
        height="250"
    />
</picture>

Override breakpoints

You can specify a different set of breakpoints for an individual image. Let's say your preconfigured breakpoints are [768], but for some image you want to use 3 sizes at breakpoints [768, 1080].

<Picture
  src={require('../images/coffee2.jpg?sizes=375,600,860')}
  breakpoints={[768,1080]}
/>

Output

<picture>
    <source
        type="image/webp"
        srcSet="/next-img/_next/static/images/coffee2-375@1x-4c059b4302106647e5331c618c63cf99.webp 375w,
                /next-img/_next/static/images/coffee2-375@2x-f840c285d2718698beef432aa0c37615.webp 750w,
                /next-img/_next/static/images/coffee2-600@1x-467d225e3195fa8c6227931e89481d06.webp 600w,
                /next-img/_next/static/images/coffee2-600@2x-6a7c588f4cfdb2704a0c24139f86392f.webp 1200w,
                /next-img/_next/static/images/coffee2-860@1x-e21d5ed84e592dc4164a0a30ccf32eb8.webp 860w,
                /next-img/_next/static/images/coffee2-860@2x-2750797d97eb48e0e3df3e6e64d63703.webp 1720w"
        sizes="(max-width: 768px) 375px, (max-width: 1080px) 600px, 860px"
    />
    <source
        type="image/jpeg"
        srcSet="/next-img/_next/static/images/coffee2-375@1x-f6cdee4a62f70e9889fb5c6c1714d519.jpg 375w,
                /next-img/_next/static/images/coffee2-375@2x-fadc29ea905d091d1c21a0e13abcb893.jpg 750w,
                /next-img/_next/static/images/coffee2-600@1x-ee5c988968fb9cd51dc8c18b6084ddd0.jpg 600w,
                /next-img/_next/static/images/coffee2-600@2x-d7dabd13f7aa1542eb38b55a498c7c91.jpg 1200w,
                /next-img/_next/static/images/coffee2-860@1x-d70fba00682d65ad65768f3f23c1864c.jpg 860w,
                /next-img/_next/static/images/coffee2-860@2x-8c1fa333dd8126c64967bb24951baa33.jpg 1720w"
        sizes="(max-width: 768px) 375px, (max-width: 1080px) 600px, 860px"
    />

    <img
        src="/next-img/_next/static/images/coffee2-375@1x-f6cdee4a62f70e9889fb5c6c1714d519.jpg"
        srcSet="/next-img/_next/static/images/coffee2-375@1x-f6cdee4a62f70e9889fb5c6c1714d519.jpg 375w,
                /next-img/_next/static/images/coffee2-375@2x-fadc29ea905d091d1c21a0e13abcb893.jpg 750w,
                /next-img/_next/static/images/coffee2-600@1x-ee5c988968fb9cd51dc8c18b6084ddd0.jpg 600w,
                /next-img/_next/static/images/coffee2-600@2x-d7dabd13f7aa1542eb38b55a498c7c91.jpg 1200w,
                /next-img/_next/static/images/coffee2-860@1x-d70fba00682d65ad65768f3f23c1864c.jpg 860w,
                /next-img/_next/static/images/coffee2-860@2x-8c1fa333dd8126c64967bb24951baa33.jpg 1720w"
        width="375"
        height="250"
    />
</picture>

Override sizes attribute

Use the sizes prop in order to get full control over specifying which image should be used at what breakpoint. This way you can specify any number of image sizes and choose to show them at any breakpoint.

<Picture
  src={require('../images/coffee3.jpg?sizes=375,600,860')}
  sizes='(max-width: 768px) 100vw, (max-width: 1180px) 600px, 860px'
/>

Output

<picture>
    <source
        type="image/webp"
        srcSet="/next-img/_next/static/images/coffee3-375@1x-655619c47b74004dd98d20a5a41fed37.webp 375w,
                /next-img/_next/static/images/coffee3-375@2x-805e1fa41a86e45ebcf2c1fec925ee18.webp 750w,
                /next-img/_next/static/images/coffee3-600@1x-3c8cee4bc6e2fb8470df7d9c1bc30fd3.webp 600w,
                /next-img/_next/static/images/coffee3-600@2x-bf903d10efc5a97b91f91abcdb7cb875.webp 1200w,
                /next-img/_next/static/images/coffee3-860@1x-c3e3ac3921d166bb49499fce389689b7.webp 860w,
                /next-img/_next/static/images/coffee3-860@2x-fddd9be273a7d4892e9d8b96a36336e7.webp 1720w"
        sizes="(max-width: 768px) 100vw, (max-width: 1180px) 600px, 860px"
    />
    <source
        type="image/jpeg"
        srcSet="/next-img/_next/static/images/coffee3-375@1x-2e44a6abd89c745f7bd8d4e6cfda2632.jpg 375w,
                /next-img/_next/static/images/coffee3-375@2x-a05c1598d8324773a577c5fae282392d.jpg 750w,
                /next-img/_next/static/images/coffee3-600@1x-47f13bbf5659b5ddd7e8cf081214f520.jpg 600w,
                /next-img/_next/static/images/coffee3-600@2x-761bda09d8a401c37047dc19910b65e4.jpg 1200w,
                /next-img/_next/static/images/coffee3-860@1x-900f1310516e2248c1df42cd0f8eaac2.jpg 860w,
                /next-img/_next/static/images/coffee3-860@2x-867e9d3ebc2dd2356f0e69f7abf4550b.jpg 1720w"
        sizes="(max-width: 768px) 100vw, (max-width: 1180px) 600px, 860px"
    />

    <img
        src="/next-img/_next/static/images/coffee3-375@1x-2e44a6abd89c745f7bd8d4e6cfda2632.jpg"
        srcSet="/next-img/_next/static/images/coffee3-375@1x-2e44a6abd89c745f7bd8d4e6cfda2632.jpg 375w,
                /next-img/_next/static/images/coffee3-375@2x-a05c1598d8324773a577c5fae282392d.jpg 750w,
                /next-img/_next/static/images/coffee3-600@1x-47f13bbf5659b5ddd7e8cf081214f520.jpg 600w,
                /next-img/_next/static/images/coffee3-600@2x-761bda09d8a401c37047dc19910b65e4.jpg 1200w,
                /next-img/_next/static/images/coffee3-860@1x-900f1310516e2248c1df42cd0f8eaac2.jpg 860w,
                /next-img/_next/static/images/coffee3-860@2x-867e9d3ebc2dd2356f0e69f7abf4550b.jpg 1720w"
        width="375"
        height="240"
    />
</picture>

Single image

You can leave out the sizes query param altogether. That will load the original image size across any device width. Note, this still outputs an image per screen density and shows the appropriate one based on the device.

<Picture src={require('../images/coffee4.jpg')} />

Output

<picture>
    <source
        type="image/webp"
        srcSet="/next-img/_next/static/images/coffee4-1720@1x-6905b434cd977bc7995ac76af96b33d6.webp 1720w,
                /next-img/_next/static/images/coffee4-1720@2x-6905b434cd977bc7995ac76af96b33d6.webp 1720w"
        sizes="1720px"
    />
    <source
        type="image/jpeg"
        srcSet="/next-img/_next/static/images/coffee4-1720@1x-1dbc6887323e3ac40f3e3fc95f375c52.jpg 1720w,
                /next-img/_next/static/images/coffee4-1720@2x-1dbc6887323e3ac40f3e3fc95f375c52.jpg 1720w"
        sizes="1720px"
    />

    <img
        src="/next-img/_next/static/images/coffee4-1720@1x-1dbc6887323e3ac40f3e3fc95f375c52.jpg"
        srcSet="/next-img/_next/static/images/coffee4-1720@1x-1dbc6887323e3ac40f3e3fc95f375c52.jpg 1720w,
                /next-img/_next/static/images/coffee4-1720@2x-1dbc6887323e3ac40f3e3fc95f375c52.jpg 1720w"
        width="1720"
        height="1227"
    />
</picture>

Art direction

You can pass an array of images to the src. This way you can show an entirely different image at each breakpoint. This changes the html output to switch between the images using the html media attribute. Note, you can take this even further by specifying multiple sizes for each image and using the sizes propto specify what should be shown when.

<Picture
  src={[
    require('../images/coffee5-s.jpg?sizes=375'),
    require('../images/coffee5-m.jpg?sizes=600'),
    require('../images/coffee5-l.jpg?sizes=860'),
  ]}
  breakpoints={[768, 1180]}
/>

Output

<picture>
    <source
        type="image/webp"
        srcSet="/next-img/_next/static/images/coffee5-s-375@1x-72100aa464bf217fafeca65a88d0d27f.webp 375w,
                /next-img/_next/static/images/coffee5-s-375@2x-45097d2d5d742bda11fdd3ca41c28045.webp 750w"
        media="(max-width: 768px)"
        sizes="375px"
    />
    <source
        type="image/jpeg"
        srcSet="/next-img/_next/static/images/coffee5-s-375@1x-2d8a12383d3a9a7893d4630e1f9b36d6.jpg 375w,
                /next-img/_next/static/images/coffee5-s-375@2x-8d38667a32d4e89ff568d1a73ee62cdc.jpg 750w"
        media="(max-width: 768px)"
        sizes="375px"
    />
    <source
        type="image/webp"
        srcSet="/next-img/_next/static/images/coffee5-m-600@1x-f9b83ac98eab2b62319ee6da25197040.webp 600w,
                /next-img/_next/static/images/coffee5-m-600@2x-c8251cb31416672eb51139dfc7d1c673.webp 1200w"
        media="(max-width: 1180px)"
        sizes="600px"
    />
    <source
        type="image/jpeg"
        srcSet="/next-img/_next/static/images/coffee5-m-600@1x-0f6970e35bd739b4dab97e5625f0da9b.jpg 600w,
                /next-img/_next/static/images/coffee5-m-600@2x-ea36c2dcedcd5c8625f7389a6cdecc2a.jpg 1200w"
        media="(max-width: 1180px)"
        sizes="600px"
    />
    <source
        type="image/webp"
        srcSet="/next-img/_next/static/images/coffee5-l-860@1x-06e8589b9b51ef588f179007c5e22afd.webp 860w,
                /next-img/_next/static/images/coffee5-l-860@2x-9b649ee7c093952c99add9968070cfa9.webp 1720w"
        sizes="860px"
    />
    <source
        type="image/jpeg"
        srcSet="/next-img/_next/static/images/coffee5-l-860@1x-8df583fcfe71990bee2e3c6c44c903c1.jpg 860w,
                /next-img/_next/static/images/coffee5-l-860@2x-47b0f075cb30d5c5186e7c3b8320aa87.jpg 1720w"
        sizes="860px"
    />

    <img
        src="/next-img/_next/static/images/coffee5-s-375@1x-2d8a12383d3a9a7893d4630e1f9b36d6.jpg"
        srcSet="/next-img/_next/static/images/coffee5-s-375@1x-2d8a12383d3a9a7893d4630e1f9b36d6.jpg 375w,
                /next-img/_next/static/images/coffee5-s-375@2x-8d38667a32d4e89ff568d1a73ee62cdc.jpg 750w"
    />
</picture>

Exact image sizes

By default, every image gets translated to 1x and 2x densities. That is, if you display the image in the browser at 800px width, then specifying ?sizes=800 will produce and show 800px wide image for low density devices and 1600px wide image for high density devices. If you'd like, however, you can specify any number of exact sizes by setting densities to 1x.

<Picture src={require('../images/coffee6.jpg?sizes=300,600,900,1200,1500&densities=1x')} sizes='100vw' />

Output

<picture>
    <source
        type="image/webp"
        srcSet="/next-img/_next/static/images/coffee6-300@1x-f6be157ecd17ef2a01bf75b53ed84ab1.webp 300w,
                /next-img/_next/static/images/coffee6-600@1x-ddea4e0de4b0a7283fe4f4e10c4673b0.webp 600w,
                /next-img/_next/static/images/coffee6-900@1x-d953d8cf2dc921fc15da5153f5078d8d.webp 900w,
                /next-img/_next/static/images/coffee6-1200@1x-4bf302858dfbd55397614362709f8777.webp 1200w,
                /next-img/_next/static/images/coffee6-1500@1x-0ef4822936e86ff6e37de5ec35b031e4.webp 1500w"
        sizes="100vw"
    />
    <source
        type="image/jpeg"
        srcSet="/next-img/_next/static/images/coffee6-300@1x-c9ff4048c6ca5b420ff4ca9668d02000.jpg 300w,
                /next-img/_next/static/images/coffee6-600@1x-95046364fee1d8a279be3c5b0a252b51.jpg 600w,
                /next-img/_next/static/images/coffee6-900@1x-e5c7862abcf3cea2bc4db71111083abd.jpg 900w,
                /next-img/_next/static/images/coffee6-1200@1x-bb13a95cf5005a1ff35624b4ad5570d9.jpg 1200w,
                /next-img/_next/static/images/coffee6-1500@1x-8f3a131c82acad9835658391bd8b046a.jpg 1500w"
        sizes="100vw"
    />

    <img
        src="/next-img/_next/static/images/coffee6-300@1x-c9ff4048c6ca5b420ff4ca9668d02000.jpg"
        srcSet="/next-img/_next/static/images/coffee6-300@1x-c9ff4048c6ca5b420ff4ca9668d02000.jpg 300w,
                /next-img/_next/static/images/coffee6-600@1x-95046364fee1d8a279be3c5b0a252b51.jpg 600w,
                /next-img/_next/static/images/coffee6-900@1x-e5c7862abcf3cea2bc4db71111083abd.jpg 900w,
                /next-img/_next/static/images/coffee6-1200@1x-bb13a95cf5005a1ff35624b4ad5570d9.jpg 1200w,
                /next-img/_next/static/images/coffee6-1500@1x-8f3a131c82acad9835658391bd8b046a.jpg 1500w"
        width="300"
        height="200"
    />
</picture>

PNG images

PNG images are supported as well. In this case, a lossless webp is outputted by default.

<Picture src={require('../images/illustration.png?sizes=480,860')} />

Output

<picture>
    <source
        type="image/webp"
        srcSet="/next-img/_next/static/images/illustration-480@1x-459b9ba8bbde7fd2aac58fd3bac8c424.webp 480w,
                /next-img/_next/static/images/illustration-480@2x-7b5fe68003250418fa5b9b92bae11e2e.webp 960w,
                /next-img/_next/static/images/illustration-860@1x-8e3be1913799ea67f42584bf81c485b1.webp 860w,
                /next-img/_next/static/images/illustration-860@2x-8a5e82ba798adbd2c5bc4ed83909c3b1.webp 1344w"
        sizes="(max-width: 768px) 480px, 860px"
    />
    <source
        type="image/png"
        srcSet="/next-img/_next/static/images/illustration-480@1x-afd1fbe56f7198e4963e633aa8a058ea.png 480w,
                /next-img/_next/static/images/illustration-480@2x-b6aefe60fb58949054dc93c37d988b9d.png 960w,
                /next-img/_next/static/images/illustration-860@1x-5985e5baa2785a9a886e54823e45c791.png 860w,
                /next-img/_next/static/images/illustration-860@2x-320a60c865f2b020456743b96b216261.png 1344w"
        sizes="(max-width: 768px) 480px, 860px"
    />

    <img
        src="/next-img/_next/static/images/illustration-480@1x-afd1fbe56f7198e4963e633aa8a058ea.png"
        srcSet="/next-img/_next/static/images/illustration-480@1x-afd1fbe56f7198e4963e633aa8a058ea.png 480w,
                /next-img/_next/static/images/illustration-480@2x-b6aefe60fb58949054dc93c37d988b9d.png 960w,
                /next-img/_next/static/images/illustration-860@1x-5985e5baa2785a9a886e54823e45c791.png 860w,
                /next-img/_next/static/images/illustration-860@2x-320a60c865f2b020456743b96b216261.png 1344w"
        width="480"
        height="257"
    />
</picture>

Other query params and component props

You can control image quality and densities in addition to specifying the sizes when importing an image. See README for full details. You can pass extra props to the Picture component, these will be forwarded to the underlying image element. This is useful for adding lazy loading, class names, and so on.

<Picture
  src={require('../images/coffee7.jpg?sizes=375,860&jpeg[quality]=10&jpeg[webp][quality]=10')}
  className='coffee'
  data-demo='coffee'
  alt='Three cups of coffee with different amounts of milk'
  loading='lazy'
/>

Output

<picture>
    <source
        type="image/webp"
        srcSet="/next-img/_next/static/images/coffee7-375@1x-eb2429cd5573fa32355020dbb58a645c.webp 375w,
                /next-img/_next/static/images/coffee7-375@2x-257a342e80eb210c461e9cbb4bf4819e.webp 750w,
                /next-img/_next/static/images/coffee7-860@1x-e8fe40f4cfdb263d81732c099b9bb248.webp 860w,
                /next-img/_next/static/images/coffee7-860@2x-bdb4b7385184a2ab188f543b06632d25.webp 1720w"
        sizes="(max-width: 768px) 375px, 860px"
    />
    <source
        type="image/jpeg"
        srcSet="/next-img/_next/static/images/coffee7-375@1x-9fc089def94d9b56d5f94b623e939cdf.jpg 375w,
                /next-img/_next/static/images/coffee7-375@2x-ac77c86b4f0db89d4ee2633319c48a31.jpg 750w,
                /next-img/_next/static/images/coffee7-860@1x-b9ad56fd623ccbe6d48e7e374edb2aec.jpg 860w,
                /next-img/_next/static/images/coffee7-860@2x-419f8a4eccf773cb9b37619fb3991991.jpg 1720w"
        sizes="(max-width: 768px) 375px, 860px"
    />

    <img
        class="coffee"
        data-demo="coffee"
        alt="Three cups of coffee with different amounts of milk"
        loading="lazy"
        src="/next-img/_next/static/images/coffee7-375@1x-9fc089def94d9b56d5f94b623e939cdf.jpg"
        srcSet="/next-img/_next/static/images/coffee7-375@1x-9fc089def94d9b56d5f94b623e939cdf.jpg 375w,
                /next-img/_next/static/images/coffee7-375@2x-ac77c86b4f0db89d4ee2633319c48a31.jpg 750w,
                /next-img/_next/static/images/coffee7-860@1x-b9ad56fd623ccbe6d48e7e374edb2aec.jpg 860w,
                /next-img/_next/static/images/coffee7-860@2x-419f8a4eccf773cb9b37619fb3991991.jpg 1720w"
        width="375"
        height="252"
    />
</picture>
Three cups of coffee with different amounts of milk