diff --git a/__tests__/src/components/CanvasThumbnail.test.js b/__tests__/src/components/CanvasThumbnail.test.js
index f3fa0904adba3805658180cf95f5ec4df2130b45..9590dd3fa66ed87cdb55da6850c68cbada1f5a97 100644
--- a/__tests__/src/components/CanvasThumbnail.test.js
+++ b/__tests__/src/components/CanvasThumbnail.test.js
@@ -3,15 +3,22 @@ import { shallow } from 'enzyme';
 import IntersectionObserver from '@researchgate/react-intersection-observer';
 import { CanvasThumbnail } from '../../../src/components/CanvasThumbnail';
 
+/**
+ * Helper function to create a shallow wrapper around CanvasThumbnail
+ */
+function createWrapper(props) {
+  return shallow(
+    <CanvasThumbnail
+      imageUrl="https://stacks.stanford.edu/image/iiif/sn904cj3429%2F12027000/full/193,/0/default.jpg"
+      {...props}
+    />,
+  );
+}
+
 describe('CanvasThumbnail', () => {
   let wrapper;
   beforeEach(() => {
-    wrapper = shallow(
-      <CanvasThumbnail
-        imageUrl="https://stacks.stanford.edu/image/iiif/sn904cj3429%2F12027000/full/193,/0/default.jpg"
-        onClick={() => {}}
-      />,
-    );
+    wrapper = createWrapper();
   });
 
   it('renders properly', () => {
@@ -32,4 +39,30 @@ describe('CanvasThumbnail', () => {
     wrapper.instance().handleIntersection({ isIntersecting: true });
     expect(wrapper.find('img').props().src).toMatch(/stacks/);
   });
+
+  it('can be constrained by maxHeight', () => {
+    wrapper = createWrapper({ maxHeight: 500 });
+
+    expect(wrapper.find('img').props().style).toMatchObject({ height: 500, width: 'auto' });
+  });
+
+  it('can be constrained by maxWidth', () => {
+    wrapper = createWrapper({ maxWidth: 500 });
+
+    expect(wrapper.find('img').props().style).toMatchObject({ height: 'auto', width: 500 });
+  });
+
+  it('can be constrained by maxWidth and maxHeight', () => {
+    wrapper = createWrapper({ maxHeight: 400, maxWidth: 500 });
+
+    expect(wrapper.find('img').props().style).toMatchObject({ height: 400, width: 500 });
+  });
+
+  it('can be constrained by maxWidth and maxHeight and a desired aspect ratio', () => {
+    wrapper = createWrapper({ maxHeight: 400, maxWidth: 500, aspectRatio: 2 });
+    expect(wrapper.find('img').props().style).toMatchObject({ height: 250, width: 500 });
+
+    wrapper = createWrapper({ maxHeight: 400, maxWidth: 500, aspectRatio: 1 });
+    expect(wrapper.find('img').props().style).toMatchObject({ height: 400, width: 400 });
+  });
 });
diff --git a/src/components/CanvasThumbnail.js b/src/components/CanvasThumbnail.js
index 87a03bbc15557e3b202903a2131371f2e3496a94..76d14f6905b082503fe7e81f2e8481b8f227bbe0 100644
--- a/src/components/CanvasThumbnail.js
+++ b/src/components/CanvasThumbnail.js
@@ -49,12 +49,41 @@ export class CanvasThumbnail extends Component {
    *
   */
   imageStyles() {
-    const { height, style } = this.props;
-    const { image } = this.state;
+    const {
+      maxHeight, maxWidth, aspectRatio, style,
+    } = this.props;
+
+    let height;
+    let width;
+
+    if (maxHeight && maxWidth && aspectRatio) {
+      const desiredAspectRatio = maxWidth / maxHeight;
+
+      // size to width
+      if (desiredAspectRatio < aspectRatio) {
+        height = maxWidth / aspectRatio;
+        width = maxWidth;
+      } else {
+        height = maxHeight;
+        width = maxHeight * aspectRatio;
+      }
+    } else if (maxHeight && maxWidth) {
+      height = maxHeight;
+      width = maxWidth;
+    } else if (maxHeight && !maxWidth) {
+      height = maxHeight;
+      width = 'auto';
+    } else if (!maxHeight && maxWidth) {
+      height = 'auto';
+      width = maxWidth;
+    } else {
+      height = 'auto';
+      width = 'auto';
+    }
 
     return {
       height,
-      width: (image && image.src) ? '100%' : '110px',
+      width,
       ...style,
     };
   }
@@ -86,14 +115,18 @@ CanvasThumbnail.defaultImgPlaceholder = 'data:image/png;base64,iVBORw0KGgoAAAANS
 CanvasThumbnail.propTypes = {
   imageUrl: PropTypes.string,
   isValid: PropTypes.bool,
-  height: PropTypes.number,
   onClick: PropTypes.func.isRequired,
+  maxHeight: PropTypes.number,
+  maxWidth: PropTypes.number,
+  aspectRatio: PropTypes.number,
   style: PropTypes.object, // eslint-disable-line react/forbid-prop-types,
 };
 
 CanvasThumbnail.defaultProps = {
   imageUrl: null,
   isValid: true,
-  height: 150,
+  maxHeight: null,
+  maxWidth: null,
+  aspectRatio: null,
   style: {},
 };
diff --git a/src/components/ThumbnailNavigation.js b/src/components/ThumbnailNavigation.js
index edd162f34c80021b2c7873d4a2c0071ae38b7534..8943df25f5732866bdcc97fb67ab7a546c8f58de 100644
--- a/src/components/ThumbnailNavigation.js
+++ b/src/components/ThumbnailNavigation.js
@@ -74,7 +74,7 @@ export class ThumbnailNavigation extends Component {
               <CanvasThumbnail
                 onClick={() => setCanvas(window.id, currentGroupings[0].index)}
                 imageUrl={new ManifestoCanvas(canvas).thumbnail(null, config.thumbnailNavigation.height)}
-                height={config.thumbnailNavigation.height}
+                maxHeight={config.thumbnailNavigation.height}
               />
             </div>
           ))}
diff --git a/src/components/WindowSideBarCanvasPanel.js b/src/components/WindowSideBarCanvasPanel.js
index cd71348bcd42def01d0cb8c92b74ded9d2827666..8f457b0779afeba32c37a5d47cf57548846c6154 100644
--- a/src/components/WindowSideBarCanvasPanel.js
+++ b/src/components/WindowSideBarCanvasPanel.js
@@ -12,13 +12,6 @@ import { getIdAndLabelOfCanvases } from '../state/selectors';
  * a panel showing the canvases for a given manifest
  */
 export class WindowSideBarCanvasPanel extends Component {
-  /**
-   * calculateScaledWidth - calculates the scaled width according to the given width and aspectRatio
-   */
-  static calculateScaledWidth(height, aspectRatio) {
-    return Math.floor(height * aspectRatio);
-  }
-
   /**
    * render
    */
@@ -48,7 +41,10 @@ export class WindowSideBarCanvasPanel extends Component {
                     <CanvasThumbnail
                       className={classNames(classes.clickable)}
                       isValid={isValid}
-                      imageUrl={manifestoCanvas.thumbnail(config.canvasNavigation.width, config.canvasNavigation.height)}
+                      imageUrl={manifestoCanvas.thumbnail(width, height)}
+                      maxHeight={config.canvasNavigation.height}
+                      maxWidth={config.canvasNavigation.width}
+                      aspectRatio={manifestoCanvas.aspectRatio}
                       onClick={onClick}
                       style={{
                         cursor: 'pointer',