diff --git a/__tests__/src/components/OpenSeadragonViewer.test.js b/__tests__/src/components/OpenSeadragonViewer.test.js
index 6c4e66698e7a8def24169fef778a8145838ece62..cce744544cc69697dac7491a23c8972e1c6dfa56 100644
--- a/__tests__/src/components/OpenSeadragonViewer.test.js
+++ b/__tests__/src/components/OpenSeadragonViewer.test.js
@@ -4,6 +4,7 @@ import OpenSeadragon from 'openseadragon';
 import { OpenSeadragonViewer } from '../../../src/components/OpenSeadragonViewer';
 import OpenSeadragonCanvasOverlay from '../../../src/lib/OpenSeadragonCanvasOverlay';
 import Annotation from '../../../src/lib/Annotation';
+import CanvasWorld from '../../../src/lib/CanvasWorld';
 
 jest.mock('openseadragon');
 jest.mock('../../../src/lib/OpenSeadragonCanvasOverlay');
@@ -34,6 +35,7 @@ describe('OpenSeadragonViewer', () => {
         updateViewport={updateViewport}
         t={k => k}
         classes={{ controls: 'controls' }}
+        canvasWorld={new CanvasWorld([])}
       >
         <div className="foo" />
       </OpenSeadragonViewer>,
@@ -108,6 +110,7 @@ describe('OpenSeadragonViewer', () => {
           viewer={{ x: 1, y: 0, zoom: 0.5 }}
           config={{}}
           updateViewport={updateViewport}
+          canvasWorld={new CanvasWorld([])}
           t={k => k}
         >
           <div className="foo" />
diff --git a/__tests__/src/components/WindowViewer.test.js b/__tests__/src/components/WindowViewer.test.js
index f66019f014df5d493b0b4f0457cf2b0191697231..1fc591a9bb0d30bab47b8aef2dc71735704cf8a9 100644
--- a/__tests__/src/components/WindowViewer.test.js
+++ b/__tests__/src/components/WindowViewer.test.js
@@ -8,7 +8,7 @@ import fixture from '../../fixtures/version-2/019.json';
 import emptyCanvasFixture from '../../fixtures/version-2/emptyCanvas.json';
 import otherContentFixture from '../../fixtures/version-2/299843.json';
 
-let canvases = manifesto.create(fixture).getSequences()[0].getCanvases();
+let currentCanvases = manifesto.create(fixture).getSequences()[0].getCanvases();
 
 let mockWindow = {
   canvasIndex: 0,
@@ -23,7 +23,7 @@ function createWrapper(props) {
       infoResponses={{}}
       fetchInfoResponse={() => {}}
       fetchAnnotation={() => {}}
-      canvases={canvases}
+      currentCanvases={[currentCanvases[1]]}
       window={mockWindow}
       {...props}
     />,
@@ -32,10 +32,8 @@ function createWrapper(props) {
 
 describe('WindowViewer', () => {
   let wrapper;
-  beforeEach(() => {
-    wrapper = createWrapper();
-  });
   it('renders properly', () => {
+    wrapper = createWrapper();
     expect(wrapper.matchesElement(
       <>
         <OSDViewer>
@@ -44,31 +42,6 @@ describe('WindowViewer', () => {
       </>,
     )).toBe(true);
   });
-  describe('currentCanvases', () => {
-    it('by default returns a single canvas', () => {
-      expect(wrapper.instance().currentCanvases().length).toEqual(1);
-    });
-    describe('book view', () => {
-      it('when the first canvas is selected', () => {
-        wrapper = createWrapper({
-          window: {
-            canvasIndex: 0,
-            view: 'book',
-          },
-        });
-        expect(wrapper.instance().currentCanvases().length).toEqual(1);
-      });
-      it('when the second canvas is selected', () => {
-        wrapper = createWrapper({
-          window: {
-            canvasIndex: 1,
-            view: 'book',
-          },
-        });
-        expect(wrapper.instance().currentCanvases().length).toEqual(2);
-      });
-    });
-  });
   describe('currentInfoResponses', () => {
     describe('returns only available infoResponses', () => {
       it('isFetching is false', () => {
@@ -131,38 +104,32 @@ describe('WindowViewer', () => {
       });
     });
   });
-  it('when view type changes', () => {
-    expect(wrapper.instance().canvasGroupings.groupings().length).toEqual(3);
-    wrapper.setProps({
-      window: {
-        canvasIndex: 0,
-        view: 'book',
-      },
-    });
-    expect(wrapper.instance().canvasGroupings.groupings().length).toEqual(2);
-  });
 
   describe('componentDidMount', () => {
     it('does not call fetchInfoResponse for a canvas that has no images', () => {
       const mockFnCanvas0 = jest.fn();
       const mockFnCanvas2 = jest.fn();
-      canvases = manifesto.create(emptyCanvasFixture).getSequences()[0].getCanvases();
+      const canvases = manifesto.create(emptyCanvasFixture).getSequences()[0].getCanvases();
+
+      currentCanvases = [canvases[0]];
+
       mockWindow = {
         canvasIndex: 0,
         view: 'single',
       };
       wrapper = createWrapper(
         {
-          canvases,
+          currentCanvases,
           fetchInfoResponse: mockFnCanvas0,
           window: mockWindow,
         },
       );
       expect(mockFnCanvas0).toHaveBeenCalledTimes(1);
 
+      currentCanvases = [canvases[2]];
       wrapper = createWrapper(
         {
-          canvases,
+          currentCanvases,
           fetchInfoResponse: mockFnCanvas2,
           window: { canvasIndex: 2, view: 'single' },
         },
@@ -171,9 +138,11 @@ describe('WindowViewer', () => {
     });
     it('calls fetchAnnotation when otherContent is present', () => {
       const mockFnAnno = jest.fn();
-      canvases = manifesto.create(otherContentFixture).getSequences()[0].getCanvases();
+      const canvases = manifesto.create(otherContentFixture).getSequences()[0].getCanvases();
+      currentCanvases = [canvases[0]];
+
       wrapper = createWrapper(
-        { canvases, fetchAnnotation: mockFnAnno },
+        { currentCanvases, fetchAnnotation: mockFnAnno },
       );
       expect(mockFnAnno).toHaveBeenCalledTimes(1);
     });
@@ -182,16 +151,17 @@ describe('WindowViewer', () => {
   describe('componentDidUpdate', () => {
     it('does not call fetchInfoResponse for a canvas that has no images', () => {
       const mockFn = jest.fn();
-      canvases = manifesto.create(emptyCanvasFixture).getSequences()[0].getCanvases();
+      const canvases = manifesto.create(emptyCanvasFixture).getSequences()[0].getCanvases();
+      currentCanvases = [canvases[2]];
       mockWindow = {
         canvasIndex: 2,
         view: 'single',
       };
       wrapper = createWrapper(
-        { canvases, fetchInfoResponse: mockFn, window: mockWindow },
+        { currentCanvases, fetchInfoResponse: mockFn, window: mockWindow },
       );
 
-      wrapper.setProps({ window: { canvasIndex: 3, view: 'single' } });
+      wrapper.setProps({ currentCanvases: [canvases[3]], window: { canvasIndex: 3, view: 'single' } });
 
       expect(mockFn).toHaveBeenCalledTimes(0);
     });
diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js
index 4c1f9533088aa6d75e18b69215982d846135eb9c..2fa636830ef3cb6f297fb21d0e29ce32c74e9f0a 100644
--- a/src/components/OpenSeadragonViewer.js
+++ b/src/components/OpenSeadragonViewer.js
@@ -132,11 +132,11 @@ export class OpenSeadragonViewer extends Component {
    * annotationsToContext - converts anontations to a canvas context
    */
   annotationsToContext(annotations) {
-    const { currentCanvases } = this.props;
+    const { canvasWorld } = this.props;
     const context = this.osdCanvasOverlay.context2d;
     annotations.forEach((annotation) => {
       annotation.resources.forEach((resource) => {
-        const offset = new CanvasWorld(currentCanvases).offsetByCanvas(resource.targetId);
+        const offset = canvasWorld.offsetByCanvas(resource.targetId);
         const fragment = resource.fragmentSelector;
         fragment[0] += offset.x;
         context.strokeStyle = 'yellow';
@@ -149,7 +149,7 @@ export class OpenSeadragonViewer extends Component {
   /**
    */
   addTileSource(tileSource, i = 0) {
-    const { currentCanvases } = this.props;
+    const { canvasWorld } = this.props;
     return new Promise((resolve, reject) => {
       if (!this.viewer) {
         return;
@@ -157,7 +157,7 @@ export class OpenSeadragonViewer extends Component {
       this.viewer.addTiledImage({
         error: event => reject(event),
         fitBounds: new OpenSeadragon.Rect(
-          ...new CanvasWorld(currentCanvases).canvasToWorldCoordinates(i),
+          ...canvasWorld.canvasToWorldCoordinates(i),
         ),
         success: event => resolve(event),
         tileSource,
@@ -197,8 +197,8 @@ export class OpenSeadragonViewer extends Component {
    * zoomToWorld - zooms the viewer to the extent of the canvas world
    */
   zoomToWorld(immediately = true) {
-    const { currentCanvases } = this.props;
-    this.fitBounds(...new CanvasWorld(currentCanvases).worldBounds(), immediately);
+    const { canvasWorld } = this.props;
+    this.fitBounds(...canvasWorld.worldBounds(), immediately);
   }
 
   /**
@@ -262,7 +262,6 @@ OpenSeadragonViewer.defaultProps = {
   annotations: [],
   children: null,
   classes: {},
-  currentCanvases: [],
   label: null,
   tileSources: [],
   viewer: null,
@@ -271,9 +270,9 @@ OpenSeadragonViewer.defaultProps = {
 
 OpenSeadragonViewer.propTypes = {
   annotations: PropTypes.arrayOf(PropTypes.object),
+  canvasWorld: PropTypes.instanceOf(CanvasWorld).isRequired,
   children: PropTypes.element,
   classes: PropTypes.object, // eslint-disable-line react/forbid-prop-types
-  currentCanvases: PropTypes.arrayOf(PropTypes.object),
   label: PropTypes.string,
   t: PropTypes.func.isRequired,
   tileSources: PropTypes.arrayOf(PropTypes.object),
diff --git a/src/components/WindowCanvasNavigationControls.js b/src/components/WindowCanvasNavigationControls.js
index f8315c7070d58e412c5cfddca70c6623ad48c5a9..db6ddaad9cb6fd1562dc5caa4da00ac99376846b 100644
--- a/src/components/WindowCanvasNavigationControls.js
+++ b/src/components/WindowCanvasNavigationControls.js
@@ -12,7 +12,7 @@ export class WindowCanvasNavigationControls extends Component {
   /** */
   render() {
     const {
-      canvases, visible, window, zoomToWorld,
+      visible, window, zoomToWorld,
     } = this.props;
 
     if (!visible) return (<></>);
@@ -20,7 +20,7 @@ export class WindowCanvasNavigationControls extends Component {
     return (
       <div className={ns('canvas-nav')}>
         <ZoomControls windowId={window.id} zoomToWorld={zoomToWorld} />
-        <ViewerNavigation window={window} canvases={canvases} />
+        <ViewerNavigation window={window} />
         <ViewerInfo windowId={window.id} />
       </div>
     );
@@ -29,7 +29,6 @@ export class WindowCanvasNavigationControls extends Component {
 
 
 WindowCanvasNavigationControls.propTypes = {
-  canvases: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
   visible: PropTypes.bool,
   window: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
   zoomToWorld: PropTypes.func.isRequired,
diff --git a/src/components/WindowViewer.js b/src/components/WindowViewer.js
index 87fabc8dc80882e46edc6007e30d8d98a4c8e7bd..65337429efa7c7d97c8c9eb0b4bb76826abdb08b 100644
--- a/src/components/WindowViewer.js
+++ b/src/components/WindowViewer.js
@@ -3,33 +3,21 @@ import PropTypes from 'prop-types';
 import OSDViewer from '../containers/OpenSeadragonViewer';
 import WindowCanvasNavigationControls from '../containers/WindowCanvasNavigationControls';
 import ManifestoCanvas from '../lib/ManifestoCanvas';
-import CanvasGroupings from '../lib/CanvasGroupings';
 
 /**
  * Represents a WindowViewer in the mirador workspace. Responsible for mounting
  * OSD and Navigation
  */
 export class WindowViewer extends Component {
-  /**
-   * @param {Object} props
-   */
-  constructor(props) {
-    super(props);
-
-    const { canvases, window } = this.props;
-    this.canvases = canvases;
-    this.canvasGroupings = new CanvasGroupings(this.canvases, window.view);
-  }
-
   /**
    * componentDidMount - React lifecycle method
    * Request the initial canvas on mount
    */
   componentDidMount() {
-    const { fetchInfoResponse, fetchAnnotation } = this.props;
+    const { currentCanvases, fetchInfoResponse, fetchAnnotation } = this.props;
 
     if (!this.infoResponseIsInStore()) {
-      this.currentCanvases().forEach((canvas) => {
+      currentCanvases.forEach((canvas) => {
         const manifestoCanvas = new ManifestoCanvas(canvas);
         const { imageInformationUri } = manifestoCanvas;
         if (imageInformationUri) {
@@ -47,11 +35,14 @@ export class WindowViewer extends Component {
    * Request a new canvas if it is needed
    */
   componentDidUpdate(prevProps) {
-    const { window, fetchInfoResponse, fetchAnnotation } = this.props;
+    const {
+      currentCanvases, window, fetchInfoResponse, fetchAnnotation,
+    } = this.props;
+
     if (prevProps.window.view !== window.view
       || (prevProps.window.canvasIndex !== window.canvasIndex && !this.infoResponseIsInStore())
     ) {
-      this.currentCanvases().forEach((canvas) => {
+      currentCanvases.forEach((canvas) => {
         const manifestoCanvas = new ManifestoCanvas(canvas);
         const { imageInformationUri } = manifestoCanvas;
         if (imageInformationUri) {
@@ -62,10 +53,6 @@ export class WindowViewer extends Component {
         });
       });
     }
-    // If the view changes, create a new instance
-    if (prevProps.window.view !== window.view) {
-      this.canvasGroupings = new CanvasGroupings(this.canvases, window.view);
-    }
   }
 
   /**
@@ -74,29 +61,22 @@ export class WindowViewer extends Component {
    * @return [Boolean]
    */
   infoResponseIsInStore() {
+    const { currentCanvases } = this.props;
+
     const responses = this.currentInfoResponses();
-    if (responses.length === this.currentCanvases().length) {
+    if (responses.length === currentCanvases.length) {
       return true;
     }
     return false;
   }
 
-  /**
-   * Uses CanvasGroupings to figure out how many and what canvases to present to
-   * a user based off of the view, number of canvases, and canvasIndex.
-   */
-  currentCanvases() {
-    const { window } = this.props;
-    return this.canvasGroupings.getCanvases(window.canvasIndex);
-  }
-
   /**
    * currentInfoResponses - Selects infoResponses that are relevent to existing
    * canvases to be displayed.
    */
   currentInfoResponses() {
-    const { infoResponses } = this.props;
-    const currentCanvases = this.currentCanvases();
+    const { currentCanvases, infoResponses } = this.props;
+
     return currentCanvases.map(canvas => (
       infoResponses[new ManifestoCanvas(canvas).imageInformationUri]
     )).filter(infoResponse => (infoResponse !== undefined
@@ -108,10 +88,12 @@ export class WindowViewer extends Component {
    * Return an image information response from the store for the correct image
    */
   tileInfoFetchedFromStore() {
+    const { currentCanvases } = this.props;
+
     const responses = this.currentInfoResponses()
       .map(infoResponse => infoResponse.json);
     // Only return actual tileSources when all current canvases have completed.
-    if (responses.length === this.currentCanvases().length) {
+    if (responses.length === currentCanvases.length) {
       return responses;
     }
     return [];
@@ -126,10 +108,9 @@ export class WindowViewer extends Component {
       <>
         <OSDViewer
           tileSources={this.tileInfoFetchedFromStore()}
-          currentCanvases={this.currentCanvases()}
           windowId={window.id}
         >
-          <WindowCanvasNavigationControls windowId={window.id} canvases={this.canvases} />
+          <WindowCanvasNavigationControls windowId={window.id} />
         </OSDViewer>
       </>
     );
@@ -137,7 +118,7 @@ export class WindowViewer extends Component {
 }
 
 WindowViewer.propTypes = {
-  canvases: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
+  currentCanvases: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
   fetchAnnotation: PropTypes.func.isRequired,
   fetchInfoResponse: PropTypes.func.isRequired,
   infoResponses: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
diff --git a/src/containers/OpenSeadragonViewer.js b/src/containers/OpenSeadragonViewer.js
index 392f9d6e64aa0aabdca3c3ed2ebe6fab1fabc799..a4426d2b4bdb414b3664d8778c235b36578fa1b5 100644
--- a/src/containers/OpenSeadragonViewer.js
+++ b/src/containers/OpenSeadragonViewer.js
@@ -6,10 +6,12 @@ import { fade } from '@material-ui/core/styles/colorManipulator';
 import { withPlugins } from '../extend';
 import { OpenSeadragonViewer } from '../components/OpenSeadragonViewer';
 import * as actions from '../state/actions';
+import CanvasWorld from '../lib/CanvasWorld';
 import {
   getAllOrSelectedAnnotations,
   getCanvasLabel,
   getSelectedAnnotationIds,
+  getSelectedCanvases,
 } from '../state/selectors';
 
 /**
@@ -17,23 +19,16 @@ import {
  * @memberof Window
  * @private
  */
-const mapStateToProps = ({
-  viewers, windows, manifests, annotations,
-}, { windowId, currentCanvases }) => ({
+const mapStateToProps = (state, { windowId }) => ({
   annotations: getAllOrSelectedAnnotations(
-    { annotations, windows },
+    state,
     windowId,
-    currentCanvases.map(c => c.id),
-    getSelectedAnnotationIds({ windows }, windowId, currentCanvases.map(c => c.id)),
+    getSelectedCanvases(state, { windowId }),
+    getSelectedAnnotationIds(state, windowId, getSelectedCanvases(state, { windowId })),
   ),
-  label: getCanvasLabel({
-    manifests,
-    windows,
-  }, {
-    canvasIndex: 'selected',
-    windowId,
-  }),
-  viewer: viewers[windowId],
+  canvasWorld: new CanvasWorld(getSelectedCanvases(state, { windowId })),
+  label: getCanvasLabel(state, { canvasIndex: 'selected', windowId }),
+  viewer: state.viewers[windowId],
 });
 
 /**
diff --git a/src/containers/ViewerNavigation.js b/src/containers/ViewerNavigation.js
index d1edbed061c9122b2d08c78cebf871e955bd42ee..e5e804d7b0aa9163a657bd7f5a2f2af663cc0c76 100644
--- a/src/containers/ViewerNavigation.js
+++ b/src/containers/ViewerNavigation.js
@@ -3,8 +3,14 @@ import { connect } from 'react-redux';
 import { withTranslation } from 'react-i18next';
 import { withPlugins } from '../extend';
 import * as actions from '../state/actions';
+import { getManifestCanvases } from '../state/selectors';
 import { ViewerNavigation } from '../components/ViewerNavigation';
 
+/** */
+const mapStateToProps = (state, { window }) => ({
+  canvases: getManifestCanvases(state, { windowId: window.id }),
+});
+
 /**
  * mapDispatchToProps - used to hook up connect to action creators
  * @memberof ManifestForm
@@ -16,7 +22,7 @@ const mapDispatchToProps = {
 
 const enhance = compose(
   withTranslation(),
-  connect(null, mapDispatchToProps),
+  connect(mapStateToProps, mapDispatchToProps),
   withPlugins('ViewerNavigation'),
   // further HOC go here
 );
diff --git a/src/containers/WindowViewer.js b/src/containers/WindowViewer.js
index 0aa4e8a7cc6cd761465f1be2810bebb7f6db1e71..1f109343d74a4d9a0dd4d78b91f3a1d3723e604e 100644
--- a/src/containers/WindowViewer.js
+++ b/src/containers/WindowViewer.js
@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
 import { withPlugins } from '../extend';
 import * as actions from '../state/actions';
 import { WindowViewer } from '../components/WindowViewer';
-import { getManifestCanvases } from '../state/selectors';
+import { getSelectedCanvases } from '../state/selectors';
 
 /**
  * mapStateToProps - to hook up connect
@@ -12,7 +12,7 @@ import { getManifestCanvases } from '../state/selectors';
  */
 const mapStateToProps = (state, { window }) => (
   {
-    canvases: getManifestCanvases(state, { windowId: window.id }),
+    currentCanvases: getSelectedCanvases(state, { windowId: window.id }),
     infoResponses: state.infoResponses,
   }
 );