diff --git a/packages/gatsby-remark-images-contentful/README.md b/packages/gatsby-remark-images-contentful/README.md
index 19c11b0e71669..d2a2185f54506 100644
--- a/packages/gatsby-remark-images-contentful/README.md
+++ b/packages/gatsby-remark-images-contentful/README.md
@@ -52,6 +52,7 @@ plugins: [
| `sizeByPixelDensity` | `false` | Analyze images' pixel density to make decisions about target image size. This is what GitHub is doing when embedding images in tickets. This is a useful setting for documentation pages with a lot of screenshots. It can have unintended side effects on high-pixel density artworks.
Example: A screenshot made on a retina screen with a resolution of 144 (e.g. Macbook) and a width of 100px, will be rendered at 50px. |
| `wrapperStyle` | | Add custom styles to the div wrapping the responsive images. Use regular CSS syntax, e.g. `margin-bottom:10px; background: red;` |
| `backgroundColor` | `white` | Set the background color of the image to match the background of your design |
+| `withWebp` | `false` | Additionally generate WebP versions alongside your chosen file format. They are added as a srcset with the appropriate mimetype and will be loaded in browsers that support the format. |
[1]: https://jmperezperez.com/medium-image-progressive-loading-placeholder/
[2]: https://code.facebook.com/posts/991252547593574/the-technology-behind-preview-photos/
diff --git a/packages/gatsby-remark-images-contentful/src/__tests__/__snapshots__/index.js.snap b/packages/gatsby-remark-images-contentful/src/__tests__/__snapshots__/index.js.snap
index 8ce88e806f5ce..f0ed1de3bf9e5 100644
--- a/packages/gatsby-remark-images-contentful/src/__tests__/__snapshots__/index.js.snap
+++ b/packages/gatsby-remark-images-contentful/src/__tests__/__snapshots__/index.js.snap
@@ -1,38 +1,32 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`it transforms HTML img tags 1`] = `
-"
-
-
-
-
-
-
-
-
-
- "
+"
+
+
+
+
+
+ "
`;
exports[`it transforms images in markdown 1`] = `
-"
-
-
-
-
-
+
+
+
-
-
-
-
- "
+
+
+ "
`;
-exports[`it transforms images with a https scheme in markdown 1`] = `
-"
-
+exports[`it transforms images in markdown with webp srcSets if option is enabled 1`] = `
+"
+
+
+
+
+
+ "
+`;
-
-
-
+
+
+
-
-
-
-
- "
+
+
+ "
`;
diff --git a/packages/gatsby-remark-images-contentful/src/__tests__/index.js b/packages/gatsby-remark-images-contentful/src/__tests__/index.js
index f8b5fbf1c77a9..87376ce5a3750 100644
--- a/packages/gatsby-remark-images-contentful/src/__tests__/index.js
+++ b/packages/gatsby-remark-images-contentful/src/__tests__/index.js
@@ -13,6 +13,7 @@ jest.mock(`../utils/`, () => {
base64: `data:image;`,
aspectRatio: 1,
srcSet: `srcSet`,
+ webpSrcSet: `webpSrcSet`,
src: `imageUrl`,
sizes: [`128px`, `250px`],
density: 140,
@@ -194,3 +195,21 @@ test(`it leaves relative HTML img tags alone`, async () => {
const nodes = await plugin(createPluginOptions(content, imagePath))
expect(nodes[0].value).toBe(content)
})
+
+test(`it transforms images in markdown with webp srcSets if option is enabled`, async () => {
+ const imagePath = `//images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg`
+ const content = `
+![image](${imagePath})
+ `.trim()
+
+ const nodes = await plugin(createPluginOptions(content, imagePath), {
+ withWebp: true,
+ })
+
+ expect(nodes.length).toBe(1)
+
+ const node = nodes.pop()
+ expect(node.type).toBe(`html`)
+ expect(node.value).toMatchSnapshot()
+ expect(node.value).not.toMatch(``)
+})
diff --git a/packages/gatsby-remark-images-contentful/src/index.js b/packages/gatsby-remark-images-contentful/src/index.js
index 3eb9374c73cb8..46d5c2e47c457 100644
--- a/packages/gatsby-remark-images-contentful/src/index.js
+++ b/packages/gatsby-remark-images-contentful/src/index.js
@@ -25,6 +25,7 @@ module.exports = async (
linkImagesToOriginal: true,
showCaptions: false,
pathPrefix,
+ withWebp: false,
}
// This will only work for markdown syntax image tags
@@ -91,20 +92,8 @@ module.exports = async (
const fileNameNoExt = fileName.replace(/\.[^/.]+$/, ``)
const defaultAlt = fileNameNoExt.replace(/[^A-Z0-9]/gi, ` `)
- // Construct new image node w/ aspect ratio placeholder
- let rawHTML = `
-
-
+ // Create our base image tag
+ let imageTag = `
-
-
- `
+ `.trim()
+
+ // if options.withWebp is enabled, generate a webp version and change the image tag to a picture tag
+ if (options.withWebp) {
+ imageTag = `
+
+ `.trim()
+ }
+
+ // Construct new image node w/ aspect ratio placeholder
+ let rawHTML = `
+
+
+ ${imageTag}
+
+
+ `.trim()
+
// Make linking to original image optional.
if (options.linkImagesToOriginal) {
rawHTML = `
-
-${rawHTML}
-
- `
+
+ ${rawHTML}
+
+ `.trim()
}
// Wrap in figure and use title as caption
diff --git a/packages/gatsby-remark-images-contentful/src/utils/index.js b/packages/gatsby-remark-images-contentful/src/utils/index.js
index d189679ad9d2f..b3ba9833ac9e9 100644
--- a/packages/gatsby-remark-images-contentful/src/utils/index.js
+++ b/packages/gatsby-remark-images-contentful/src/utils/index.js
@@ -51,10 +51,18 @@ const buildResponsiveSizes = async ({ metadata, imageUrl, options = {} }) => {
.map(size => `${imageUrl}?w=${Math.round(size)} ${Math.round(size)}w`)
.join(`,\n`)
+ const webpSrcSet = filteredSizes
+ .map(
+ size => `${imageUrl}?fm=webp&w=${Math.round(size)} ${Math.round(size)}w`
+ )
+ .join(`,\n`)
+
+ // TODO think about a better structure to save srcset types instead of adding them to the root
return {
base64: base64Img,
aspectRatio,
srcSet,
+ webpSrcSet,
src: imageUrl,
sizes: options.sizes,
density,