diff --git a/__tests__/src/lib/ThumbnailFactory.test.js b/__tests__/src/lib/ThumbnailFactory.test.js
index be543fc6e39372e788f9beadf1252f4bd655ba8d..53f8daf4a1bc3b7b2f3e6a1080a9aecfa0d8b1ae 100644
--- a/__tests__/src/lib/ThumbnailFactory.test.js
+++ b/__tests__/src/lib/ThumbnailFactory.test.js
@@ -248,6 +248,66 @@ describe('getThumbnail', () => {
   });
 });
 
+describe('picking the best format', () => {
+  const url = 'http://example.com';
+
+  it('defaults to jpg', () => {
+    const myCanvas = {
+      ...canvas.__jsonld,
+      thumbnail: {
+        height: 100,
+        id: 'arbitrary-url',
+        service: [{
+          id: url,
+          profile: 'level2',
+          type: 'ImageService3',
+        }],
+        width: 100,
+      },
+    };
+    expect(createSubject(myCanvas, 'Canvas'))
+      .toMatchObject({ url: `${url}/full/,120/0/default.jpg` });
+  });
+
+  it('uses the preferred format of the service', () => {
+    const myCanvas = {
+      ...canvas.__jsonld,
+      thumbnail: {
+        height: 100,
+        id: 'arbitrary-url',
+        service: [{
+          id: url,
+          preferredFormats: ['webp'],
+          profile: 'level2',
+          type: 'ImageService3',
+        }],
+        width: 100,
+      },
+    };
+    expect(createSubject(myCanvas, 'Canvas'))
+      .toMatchObject({ url: `${url}/full/,120/0/default.webp` });
+  });
+
+  it('can be filtered by application preferred formats', () => {
+    const myCanvas = {
+      ...canvas.__jsonld,
+      thumbnail: {
+        height: 100,
+        id: 'arbitrary-url',
+        service: [{
+          id: url,
+          preferredFormats: ['webp', 'png'],
+          profile: 'level2',
+          type: 'ImageService3',
+        }],
+        width: 100,
+      },
+    };
+    expect(createSubject(myCanvas, 'Canvas', { preferredFormats: ['png', 'jpg'] }))
+      .toMatchObject({ url: `${url}/full/,120/0/default.png` });
+  });
+});
+
 describe('selectBestImageSize', () => {
   const targetWidth = 120;
   const targetHeight = 120;
diff --git a/src/components/IIIFThumbnail.js b/src/components/IIIFThumbnail.js
index b4b044a56100026da7f408e87ec05b5161a72aad..c942f4824231739d722ab08c9682abdc3165148f 100644
--- a/src/components/IIIFThumbnail.js
+++ b/src/components/IIIFThumbnail.js
@@ -115,12 +115,12 @@ export class IIIFThumbnail extends Component {
   /** */
   image() {
     const {
-      thumbnail, resource, maxHeight, maxWidth,
+      thumbnail, resource, maxHeight, maxWidth, thumbnailsConfig,
     } = this.props;
 
     if (thumbnail) return thumbnail;
 
-    const image = getThumbnail(resource, { maxHeight, maxWidth });
+    const image = getThumbnail(resource, { ...thumbnailsConfig, maxHeight, maxWidth });
 
     if (image && image.url) return image;
 
@@ -189,6 +189,7 @@ IIIFThumbnail.propTypes = {
     url: PropTypes.string.isRequired,
     width: PropTypes.number,
   }),
+  thumbnailsConfig: PropTypes.object, // eslint-disable-line react/forbid-prop-types
   variant: PropTypes.oneOf(['inside', 'outside']),
 };
 
@@ -203,5 +204,6 @@ IIIFThumbnail.defaultProps = {
   maxWidth: null,
   style: {},
   thumbnail: null,
+  thumbnailsConfig: {},
   variant: null,
 };
diff --git a/src/config/settings.js b/src/config/settings.js
index cf1115ad646b7cb66d05385b8baedcf8c1b0789a..43ec317f4b37dccf659a67a5e4fd3a67d00f7aa2 100644
--- a/src/config/settings.js
+++ b/src/config/settings.js
@@ -298,6 +298,9 @@ export default {
     // ../lib/MiradorViewer.js `windowAction`
     */
   ],
+  thumbnails: {
+    preferredFormats: ['jpg', 'png', 'webp', 'tif'],
+  },
   thumbnailNavigation: {
     defaultPosition: 'off', // Which position for the thumbnail navigation to be be displayed. Other possible values are "far-bottom" or "far-right"
     displaySettings: true, // Display the settings for this in WindowTopMenu
diff --git a/src/containers/IIIFThumbnail.js b/src/containers/IIIFThumbnail.js
index 71c95d0a4900ee5b46b177743c6952356b6e5644..c596b508cd92fee25b69b5e09e198011182363e7 100644
--- a/src/containers/IIIFThumbnail.js
+++ b/src/containers/IIIFThumbnail.js
@@ -1,9 +1,21 @@
 import { compose } from 'redux';
+import { connect } from 'react-redux';
 import { withTranslation } from 'react-i18next';
 import { withStyles } from '@material-ui/core/styles';
 import { withPlugins } from '../extend/withPlugins';
+import {
+  getConfig,
+} from '../state/selectors';
 import { IIIFThumbnail } from '../components/IIIFThumbnail';
 
+/**
+ * mapStateToProps - to hook up connect
+ * @private
+ */
+const mapStateToProps = (state) => ({
+  thumbnailsConfig: getConfig(state).thumbnails,
+});
+
 /**
  * Styles for withStyles HOC
  */
@@ -50,6 +62,7 @@ const styles = theme => ({
 const enhance = compose(
   withStyles(styles),
   withTranslation(),
+  connect(mapStateToProps),
   withPlugins('IIIFThumbnail'),
 );
 
diff --git a/src/lib/ThumbnailFactory.js b/src/lib/ThumbnailFactory.js
index 8d8d1370ac167d3860c4633820f5a3ac564656fd..67f8a5787467a7b7ce3d008508337b6690364492 100644
--- a/src/lib/ThumbnailFactory.js
+++ b/src/lib/ThumbnailFactory.js
@@ -197,7 +197,7 @@ class ThumbnailFactory {
     const region = 'full';
     const quality = Utils.getImageQuality(service.getProfile());
     const id = service.id.replace(/\/+$/, '');
-    const format = 'jpg';
+    const format = this.getFormat(service);
     return {
       height,
       url: [id, region, size, 0, `${quality}.${format}`].join('/'),
@@ -205,6 +205,41 @@ class ThumbnailFactory {
     };
   }
 
+  /**
+   * Figure out what format thumbnail to use by looking at the preferred formats
+   * on offer, and selecting a format shared in common with the application's
+   * preferred format list.
+   *
+   * Fall back to jpg, which is required to work for all IIIF services.
+   */
+  getFormat(service) {
+    const { preferredFormats = [] } = this.iiifOpts;
+    const servicePreferredFormats = service.getProperty('preferredFormats');
+
+    if (!servicePreferredFormats) return 'jpg';
+
+    const filteredFormats = servicePreferredFormats.filter(
+      value => preferredFormats.includes(value),
+    );
+
+    // this is a format found in common between the preferred formats of the service
+    // and the application
+    if (filteredFormats[0]) return filteredFormats[0];
+
+    // IIIF Image API guarantees jpg support; if it wasn't provided by the service
+    // but the application is fine with it, we might as well try it.
+    if (!servicePreferredFormats.includes('jpg') && preferredFormats.includes('jpg')) {
+      return 'jpg';
+    }
+
+    // there were no formats in common, and the application didn't want jpg... so
+    // just trust that the IIIF service is advertising something useful?
+    if (servicePreferredFormats[0]) return servicePreferredFormats[0];
+
+    // JPG support is guaranteed by the spec, so it's a good worst-case fallback
+    return 'jpg';
+  }
+
   /**
    * Determines the content resource from which to derive a thumbnail to represent a given resource.
    * This method is recursive.
diff --git a/src/state/selectors/manifests.js b/src/state/selectors/manifests.js
index 63a726680aa8929779c2888c5cc1635b424bdbb5..e9d7c2bbcb13036f2ed27be1abb280a90750a2c7 100644
--- a/src/state/selectors/manifests.js
+++ b/src/state/selectors/manifests.js
@@ -234,10 +234,13 @@ export const getRights = createSelector(
 */
 export function getManifestThumbnail(state, props) {
   const manifest = getManifestoInstance(state, props);
+  const { thumbnails = {} } = getConfig(state);
 
   if (!manifest) return undefined;
 
-  const thumbnail = getThumbnail(manifest, { maxHeight: 80, maxWidth: 120 });
+  const thumbnail = getThumbnail(manifest, {
+    maxHeight: 80, maxWidth: 120, preferredFormats: thumbnails.preferredFormats,
+  });
 
   return thumbnail && thumbnail.url;
 }