diff --git a/__tests__/src/actions/window.test.js b/__tests__/src/actions/window.test.js
index 42c2ad3d4e8ad38095a53c0cc0d5fce898b8df23..e7599b5d4b8f3b65aec0f6537d67f16ce662bba3 100644
--- a/__tests__/src/actions/window.test.js
+++ b/__tests__/src/actions/window.test.js
@@ -54,7 +54,6 @@ describe('window actions', () => {
           canvasIndex: 1,
           collectionIndex: 0,
           id: 'helloworld',
-          layoutOrder: 3,
           manifestId: null,
           maximized: false,
           rangeId: null,
@@ -73,7 +72,7 @@ describe('window actions', () => {
             sideBarOpenByDefault: false,
           },
         },
-        windows: { a: {}, b: {} },
+        workspace: { windowIds: ['a', 'b'] },
       };
 
       const mockDispatch = jest.fn(() => ({}));
@@ -108,7 +107,7 @@ describe('window actions', () => {
             defaultSideBarPanel: 'info',
           },
         },
-        windows: {},
+        workspace: {},
       };
 
       const mockDispatch = jest.fn(() => ({}));
@@ -138,7 +137,7 @@ describe('window actions', () => {
             defaultSideBarPanel: null,
           },
         },
-        windows: {},
+        workspace: {},
       };
 
       const mockDispatch = jest.fn(() => ({}));
@@ -168,7 +167,7 @@ describe('window actions', () => {
             defaultSideBarPanel: null,
           },
         },
-        windows: {},
+        workspace: {},
       };
 
       const mockDispatch = jest.fn(() => ({}));
diff --git a/__tests__/src/components/WorkspaceMosaic.test.js b/__tests__/src/components/WorkspaceMosaic.test.js
index efc150d3385528fe8abb6ee2f57c046f8ae8bf80..8224bd1ef6dc92ccebda8c06b8fcef04265e8e1b 100644
--- a/__tests__/src/components/WorkspaceMosaic.test.js
+++ b/__tests__/src/components/WorkspaceMosaic.test.js
@@ -9,7 +9,7 @@ function createWrapper(props) {
   return shallow(
     <WorkspaceMosaic
       classes={{}}
-      windows={{}}
+      windowIds={[]}
       workspaceId="foo"
       updateWorkspaceMosaicLayout={() => {}}
       {...props}
@@ -18,10 +18,10 @@ function createWrapper(props) {
 }
 
 describe('WorkspaceMosaic', () => {
-  const windows = { 1: { id: 1 }, 2: { id: 2 } };
+  const windowIds = ['1', '2'];
   let wrapper;
   beforeEach(() => {
-    wrapper = createWrapper({ windows });
+    wrapper = createWrapper({ windowIds });
   });
   it('should render properly with an initialValue', () => {
     expect(wrapper.find(MosaicWithoutDragDropContext).length).toEqual(1);
@@ -34,10 +34,10 @@ describe('WorkspaceMosaic', () => {
       const updateWorkspaceMosaicLayout = jest.fn();
       wrapper = createWrapper({
         updateWorkspaceMosaicLayout,
-        windows,
+        windowIds,
       });
 
-      wrapper.setProps({ windows: { ...windows, 3: { id: 3 } } });
+      wrapper.setProps({ windowIds: [...windowIds, '3'] });
 
       expect(updateWorkspaceMosaicLayout).toHaveBeenCalled();
     });
@@ -46,19 +46,19 @@ describe('WorkspaceMosaic', () => {
       wrapper = createWrapper({
         layout: { first: 1, second: 2 },
         updateWorkspaceMosaicLayout,
-        windows,
+        windowIds,
       });
       wrapper.instance().windowPaths = { 2: ['second'] };
-      wrapper.setProps({ windows: { 1: { id: 1 } } });
+      wrapper.setProps({ windowIds: [1] });
       expect(updateWorkspaceMosaicLayout).toHaveBeenLastCalledWith(1);
     });
     it('when no windows remain', () => {
       const updateWorkspaceMosaicLayout = jest.fn();
       wrapper = createWrapper({
         updateWorkspaceMosaicLayout,
-        windows,
+        windowIds,
       });
-      wrapper.setProps({ windows: {} });
+      wrapper.setProps({ windowIds: [] });
       expect(updateWorkspaceMosaicLayout).toHaveBeenLastCalledWith(null);
     });
     it('when the new and old layouts are the same', () => {
@@ -66,9 +66,9 @@ describe('WorkspaceMosaic', () => {
       wrapper = createWrapper({
         layout: { first: 1, second: 2 },
         updateWorkspaceMosaicLayout,
-        windows,
+        windowIds,
       });
-      wrapper.setProps({ layout: { first: 1, second: 2 }, windows });
+      wrapper.setProps({ layout: { first: 1, second: 2 }, windowIds });
       expect(updateWorkspaceMosaicLayout).toHaveBeenCalledTimes(1);
     });
   });
@@ -80,21 +80,21 @@ describe('WorkspaceMosaic', () => {
   });
   describe('determineWorkspaceLayout', () => {
     it('when window ids do not match workspace layout', () => {
-      wrapper = createWrapper({ layout: {}, windows });
+      wrapper = createWrapper({ layout: {}, windowIds });
       expect(wrapper.instance().determineWorkspaceLayout()).toMatchObject({
         direction: 'row', first: '1', second: '2',
       });
     });
     it('by default use workspace.layout', () => {
-      wrapper = createWrapper({ layout: {}, windows: { foo: 'bar' } });
+      wrapper = createWrapper({ layout: {}, windowIds: ['foo'] });
       expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo');
     });
     it('generates a new layout if windows do not match current layout', () => {
-      wrapper = createWrapper({ layout: { first: 'foo', second: 'bark' }, windows: { foo: 'bar' } });
+      wrapper = createWrapper({ layout: { first: 'foo', second: 'bark' }, windowIds: ['foo'] });
       expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo');
     });
     it('when window ids match workspace layout', () => {
-      wrapper = createWrapper({ layout: {}, windows: { foo: { id: 'foo' } } });
+      wrapper = createWrapper({ layout: {}, windowIds: ['foo'] });
       expect(wrapper.instance().determineWorkspaceLayout()).toBe('foo');
     });
   });
@@ -124,7 +124,7 @@ describe('WorkspaceMosaic', () => {
       const updateWorkspaceMosaicLayout = jest.fn();
       wrapper = createWrapper({
         updateWorkspaceMosaicLayout,
-        windows,
+        windowIds,
       });
 
       wrapper.instance().mosaicChange();
diff --git a/__tests__/src/lib/MiradorViewer.test.js b/__tests__/src/lib/MiradorViewer.test.js
index 17c55354396e5527cbb6647cd4d218c707944851..75e2d67b7a05dcc1879f75416445919c0f1dec1d 100644
--- a/__tests__/src/lib/MiradorViewer.test.js
+++ b/__tests__/src/lib/MiradorViewer.test.js
@@ -44,8 +44,6 @@ describe('MiradorViewer', () => {
       expect(Object.keys(windowIds).length).toBe(2);
       expect(windows[windowIds[0]].canvasId).toBe('https://iiif.harvardartmuseums.org/manifests/object/299843/canvas/canvas-47174892');
       expect(windows[windowIds[1]].canvasId).toBe(undefined);
-      expect(windows[windowIds[0]].layoutOrder).toBe(0);
-      expect(windows[windowIds[1]].layoutOrder).toBe(1);
       expect(windows[windowIds[0]].thumbnailNavigationPosition).toBe('far-bottom');
       expect(windows[windowIds[1]].thumbnailNavigationPosition).toBe(undefined);
       expect(windows[windowIds[0]].view).toBe(undefined);
diff --git a/__tests__/src/sagas/app.test.js b/__tests__/src/sagas/app.test.js
index 807e401cea53e8e28651c4b580b79613b93ea760..ce4467d0939cee103190f468ddb0deaba5f3e2d5 100644
--- a/__tests__/src/sagas/app.test.js
+++ b/__tests__/src/sagas/app.test.js
@@ -69,10 +69,10 @@ describe('app-level sagas', () => {
       return expectSaga(importConfig, action)
         .provide([
           [call(addWindow, {
-            id: 'x', layoutOrder: 0, manifestId: 'a', thumbnailNavigationPosition: undefined,
+            id: 'x', manifestId: 'a', thumbnailNavigationPosition: undefined,
           }), { type: 'thunk1' }],
           [call(addWindow, {
-            id: 'y', layoutOrder: 1, manifestId: 'b', thumbnailNavigationPosition: undefined,
+            id: 'y', manifestId: 'b', thumbnailNavigationPosition: undefined,
           }), { type: 'thunk2' }],
         ])
         .put({ type: 'thunk1' })
diff --git a/__tests__/src/selectors/getters.test.js b/__tests__/src/selectors/getters.test.js
index 725af884f4c37aa18e00d78f7a45d629ed35b4f7..eabadc67310b7b09163697da979fc1e4c6ae7442 100644
--- a/__tests__/src/selectors/getters.test.js
+++ b/__tests__/src/selectors/getters.test.js
@@ -3,6 +3,7 @@ import {
   getViewer,
   getWindowManifests,
   getWindows,
+  getCatalog,
 } from '../../../src/state/selectors/getters';
 
 describe('getManifest()', () => {
@@ -92,3 +93,16 @@ describe('getViewer', () => {
     });
   });
 });
+
+describe('getCatalog', () => {
+  const catalog = [{ a: 1 }];
+  const state = {
+    catalog,
+  };
+
+  it('should return companion windows for a given window id', () => {
+    const received = getCatalog(state);
+
+    expect(received).toEqual(catalog);
+  });
+});
diff --git a/__tests__/src/selectors/windows.test.js b/__tests__/src/selectors/windows.test.js
index c8d5293708c3fe5127431db14e8718b0318171e6..0d86a9dc3f3289e79a4f2ef075749a88325e1311 100644
--- a/__tests__/src/selectors/windows.test.js
+++ b/__tests__/src/selectors/windows.test.js
@@ -24,6 +24,18 @@ describe('getWindowConfig', () => {
 
     expect(getWindowConfig(state, { windowId: 'a' })).toEqual({ a: '1', b: '3', c: '4' });
   });
+  it('gracefully handles missing windows', () => {
+    const state = {
+      config: {
+        window: { a: '1', b: '2' },
+      },
+      windows: {
+        a: {},
+      },
+    };
+
+    expect(getWindowConfig(state, { windowId: 'c' })).toEqual({ a: '1', b: '2' });
+  });
 });
 
 describe('getMaximizedWindowsIds', () => {
@@ -155,7 +167,6 @@ describe('getWindowDraggability', () => {
   describe('in elastic mode', () => {
     it('is always true', () => {
       const state = {
-        windows: {},
         workspace: { type: 'elastic' },
       };
       const props = {};
@@ -167,8 +178,7 @@ describe('getWindowDraggability', () => {
   describe('in non-elastic mode', () => {
     it('is false if there is only one window', () => {
       const state = {
-        windows: { abc123: {} },
-        workspace: { type: 'mosaic' },
+        workspace: { type: 'mosaic', windowIds: ['abc123'] },
       };
       const props = { windowId: 'abc123' };
 
@@ -178,7 +188,7 @@ describe('getWindowDraggability', () => {
     it('is false when the window is maximized', () => {
       const state = {
         windows: { abc123: { maximized: true }, abc321: { maximized: false } },
-        workspace: { type: 'mosaic' },
+        workspace: { type: 'mosaic', windowIds: ['abc123', 'abc321'] },
       };
       const props = { windowId: 'abc123' };
 
@@ -188,7 +198,7 @@ describe('getWindowDraggability', () => {
     it('is true if there are many windows (as long as the window is not maximized)', () => {
       const state = {
         windows: { abc123: { maximized: false }, abc321: { maximized: false } },
-        workspace: { type: 'mosaic' },
+        workspace: { type: 'mosaic', windowIds: ['abc123', 'abc321'] },
       };
       const props = { windowId: 'abc123' };
 
diff --git a/__tests__/src/selectors/workspace.test.js b/__tests__/src/selectors/workspace.test.js
index 8e2823c47811f6687793b26a8fede4462906110d..f4f78c5923d23d33099bbe83abc267d6965c0e46 100644
--- a/__tests__/src/selectors/workspace.test.js
+++ b/__tests__/src/selectors/workspace.test.js
@@ -1,6 +1,7 @@
 import {
   getFullScreenEnabled,
   getWorkspaceType,
+  isFocused,
 } from '../../../src/state/selectors';
 
 describe('getFullScreenEnabled', () => {
@@ -16,3 +17,14 @@ describe('getWorkspaceType', () => {
     expect(getWorkspaceType(state)).toEqual('elastic');
   });
 });
+
+describe('isFocused', () => {
+  it('is true if the window has focus', () => {
+    const state = { workspace: { focusedWindowId: 'a' } };
+    expect(isFocused(state, { windowId: 'a' })).toEqual(true);
+  });
+  it('is false if the window does not has focus', () => {
+    const state = { workspace: { focusedWindowId: 'a' } };
+    expect(isFocused(state, { windowId: 'b' })).toEqual(false);
+  });
+});
diff --git a/src/components/SidebarIndexThumbnail.js b/src/components/SidebarIndexThumbnail.js
index 44e095cf607afeee95407a8973f5d525bb5f9b7f..c2ab64128277c054228e349045a06de5299faf50 100644
--- a/src/components/SidebarIndexThumbnail.js
+++ b/src/components/SidebarIndexThumbnail.js
@@ -9,7 +9,7 @@ export class SidebarIndexThumbnail extends Component {
   /** */
   render() {
     const {
-      classes, config, otherCanvas, canvas,
+      classes, otherCanvas, canvas, height, width,
     } = this.props;
 
     return (
@@ -18,8 +18,8 @@ export class SidebarIndexThumbnail extends Component {
           <IIIFThumbnail
             resource={otherCanvas}
             className={classNames(classes.clickable)}
-            maxHeight={config.canvasNavigation.height}
-            maxWidth={config.canvasNavigation.width}
+            maxHeight={height}
+            maxWidth={width}
           />
         </div>
         <Typography
@@ -36,6 +36,12 @@ export class SidebarIndexThumbnail extends Component {
 SidebarIndexThumbnail.propTypes = {
   canvas: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
   classes: PropTypes.objectOf(PropTypes.string).isRequired,
-  config: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  height: PropTypes.number,
   otherCanvas: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  width: PropTypes.number,
+};
+
+SidebarIndexThumbnail.defaultProps = {
+  height: undefined,
+  width: undefined,
 };
diff --git a/src/components/WorkspaceMosaic.js b/src/components/WorkspaceMosaic.js
index 93476bd8058722498640eb346aaed8f5ce7a0ad2..a7003be9c47c0f55feae4ec1601ea43cb2f6ea90 100644
--- a/src/components/WorkspaceMosaic.js
+++ b/src/components/WorkspaceMosaic.js
@@ -42,25 +42,24 @@ export class WorkspaceMosaic extends React.Component {
 
   /** */
   componentDidUpdate(prevProps) {
-    const { windows, layout, updateWorkspaceMosaicLayout } = this.props;
-    const prevWindows = Object.keys(prevProps.windows);
-    const currentWindows = Object.keys(windows);
+    const { windowIds, layout, updateWorkspaceMosaicLayout } = this.props;
+    const prevWindows = prevProps.windowIds;
     // Handles when Windows are added (not via Add Resource UI) Could be a workspace import
-    if (!currentWindows.every(e => prevWindows.includes(e))) {
+    if (!windowIds.every(e => prevWindows.includes(e))) {
       const newLayout = this.determineWorkspaceLayout();
       if (!isEqual(newLayout, layout)) updateWorkspaceMosaicLayout(newLayout);
       return;
     }
-    // console.log(prevWindows, currentWindows);
+
     // Handles when Windows are removed from the state
-    if (!prevWindows.every(e => currentWindows.includes(e))) {
+    if (!prevWindows.every(e => windowIds.includes(e))) {
       // There are no more remaining Windows, just return an empty layout
-      if (currentWindows.length === 0) {
+      if (windowIds.length === 0) {
         updateWorkspaceMosaicLayout(null);
         return;
       }
 
-      const removedWindows = difference(prevWindows, currentWindows);
+      const removedWindows = difference(prevWindows, windowIds);
       const newLayout = new MosaicLayout(layout);
       newLayout.removeWindows(removedWindows, this.windowPaths);
       updateWorkspaceMosaicLayout(newLayout.layout);
@@ -80,26 +79,24 @@ export class WorkspaceMosaic extends React.Component {
    * Used to determine whether or not a "new" layout should be autogenerated.
    */
   determineWorkspaceLayout() {
-    const { windows, layout } = this.props;
-    const sortedWindows = toPairs(windows)
-      .sort((a, b) => a.layoutOrder - b.layoutOrder).map(val => val[0]);
+    const { windowIds, layout } = this.props;
     const leaveKeys = getLeaves(layout);
     // Windows were added
-    if (!sortedWindows.every(e => leaveKeys.includes(e))) {
+    if (!windowIds.every(e => leaveKeys.includes(e))) {
       // No current layout, so just generate a new one
       if (leaveKeys.length < 2) {
-        return createBalancedTreeFromLeaves(sortedWindows);
+        return createBalancedTreeFromLeaves(windowIds);
       }
       // Add new windows to layout
-      const addedWindows = difference(sortedWindows, leaveKeys);
+      const addedWindows = difference(windowIds, leaveKeys);
       const newLayout = new MosaicLayout(layout);
       newLayout.addWindows(addedWindows);
       return newLayout.layout;
     }
     // Windows were removed (perhaps in a different Workspace). We don't have a
     // way to reconfigure.. so we have to random generate
-    if (!leaveKeys.every(e => sortedWindows.includes(e))) {
-      return createBalancedTreeFromLeaves(sortedWindows);
+    if (!leaveKeys.every(e => windowIds.includes(e))) {
+      return createBalancedTreeFromLeaves(windowIds);
     }
     return layout;
   }
@@ -117,21 +114,20 @@ export class WorkspaceMosaic extends React.Component {
    * Render a tile (Window) in the Mosaic.
    */
   tileRenderer(id, path) {
-    const { windows, workspaceId } = this.props;
-    const window = windows[id];
-    if (!window) return null;
-    this.bookkeepPath(window.id, path);
+    const { windowIds, workspaceId } = this.props;
+    if (!windowIds.includes(id)) return null;
+    this.bookkeepPath(id, path);
     return (
       <MosaicWindow
         toolbarControls={this.toolbarControls}
         additionalControls={this.additionalControls}
         path={path}
-        windowId={window.id}
+        windowId={id}
         renderPreview={WorkspaceMosaic.renderPreview}
       >
         <Window
-          key={`${window.id}-${workspaceId}`}
-          windowId={window.id}
+          key={`${id}-${workspaceId}`}
+          windowId={id}
         />
       </MosaicWindow>
     );
@@ -167,10 +163,11 @@ WorkspaceMosaic.propTypes = {
     [PropTypes.object, PropTypes.string],
   ), // eslint-disable-line react/forbid-prop-types
   updateWorkspaceMosaicLayout: PropTypes.func.isRequired,
-  windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  windowIds: PropTypes.arrayOf(PropTypes.string),
   workspaceId: PropTypes.string.isRequired,
 };
 
 WorkspaceMosaic.defaultProps = {
   layout: undefined,
+  windowIds: [],
 };
diff --git a/src/config/settings.js b/src/config/settings.js
index 42d8cdbedcb73e5bc8284d7551f99ee63d0fb72e..010bcaa309845ae6db79391f50a509d6d3d1b5cc 100644
--- a/src/config/settings.js
+++ b/src/config/settings.js
@@ -1,6 +1,9 @@
 import { v4 as uuid } from 'uuid';
 
 export default {
+  state: {
+    // slice: 'mirador' // Configure the top-level slice of state for mirador selectors
+  },
   canvasNavigation: { // Set the hight and width of canvas thumbnails in the  CanvasNavigation companion window
     height: 50,
     width: 50,
diff --git a/src/containers/AccessTokenSender.js b/src/containers/AccessTokenSender.js
index 61a4edcce3c6ed63c55c819d98e87ce96f049598..eb8b51fea3aa7441fd6b2b04c43b33eec34a8b93 100644
--- a/src/containers/AccessTokenSender.js
+++ b/src/containers/AccessTokenSender.js
@@ -2,6 +2,7 @@ import { compose } from 'redux';
 import { connect } from 'react-redux';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
+import { getAccessTokens } from '../state/selectors';
 import { AccessTokenSender } from '../components/AccessTokenSender';
 
 /**
@@ -9,8 +10,8 @@ import { AccessTokenSender } from '../components/AccessTokenSender';
  * @memberof App
  * @private
  */
-const mapStateToProps = ({ accessTokens }) => ({
-  url: accessTokens && (Object.values(accessTokens).find(e => e.isFetching) || {}).id,
+const mapStateToProps = (state) => ({
+  url: (Object.values(getAccessTokens(state)).find(e => e.isFetching) || {}).id,
 });
 
 /**
diff --git a/src/containers/AnnotationSettings.js b/src/containers/AnnotationSettings.js
index 530096e786fba5bcfff789cf4b1c7a4119fa2461..1c1dd8d0c616100d50018ddd2e6da39bd7d27006 100644
--- a/src/containers/AnnotationSettings.js
+++ b/src/containers/AnnotationSettings.js
@@ -16,7 +16,7 @@ const mapStateToProps = (state, { windowId }) => ({
   displayAll: getWindow(state, { windowId }).highlightAllAnnotations,
   displayAllDisabled: getAnnotationResourcesByMotivation(
     state,
-    { motivations: state.config.annotations.filteredMotivations, windowId },
+    { windowId },
   ).length < 2,
 });
 
diff --git a/src/containers/AppProviders.js b/src/containers/AppProviders.js
index 226621be110d055cfcd56670824136e0ec36cacf..9e2c86ca3fe7b31fbed5fe4c20998835478f12b0 100644
--- a/src/containers/AppProviders.js
+++ b/src/containers/AppProviders.js
@@ -2,7 +2,7 @@ import { compose } from 'redux';
 import { connect } from 'react-redux';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
-import { getTheme } from '../state/selectors';
+import { getConfig, getTheme, getFullScreenEnabled } from '../state/selectors';
 import { AppProviders } from '../components/AppProviders';
 
 /**
@@ -12,11 +12,11 @@ import { AppProviders } from '../components/AppProviders';
  */
 const mapStateToProps = state => (
   {
-    classPrefix: state.config.classPrefix,
-    isFullscreenEnabled: state.workspace.isFullscreenEnabled,
-    language: state.config.language,
+    classPrefix: getConfig(state).classPrefix,
+    isFullscreenEnabled: getFullScreenEnabled(state),
+    language: getConfig(state).language,
     theme: getTheme(state),
-    translations: state.config.translations,
+    translations: getConfig(state).translations,
   }
 );
 
diff --git a/src/containers/AuthenticationSender.js b/src/containers/AuthenticationSender.js
index c574259646d4a9461404d9058d14b274936bf1f3..97ab2847d775bc1e5467ac1f47f91ff88c4b12d7 100644
--- a/src/containers/AuthenticationSender.js
+++ b/src/containers/AuthenticationSender.js
@@ -2,6 +2,7 @@ import { compose } from 'redux';
 import { connect } from 'react-redux';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
+import { getAuth, getConfig } from '../state/selectors';
 import { AuthenticationSender } from '../components/AuthenticationSender';
 
 /**
@@ -9,9 +10,9 @@ import { AuthenticationSender } from '../components/AuthenticationSender';
  * @memberof App
  * @private
  */
-const mapStateToProps = ({ auth, config }) => ({
-  center: config.window.authNewWindowCenter,
-  url: auth && (Object.values(auth).find(e => e.isFetching && e.profile !== 'http://iiif.io/api/auth/1/external') || {}).id,
+const mapStateToProps = (state) => ({
+  center: getConfig(state).window.authNewWindowCenter,
+  url: (Object.values(getAuth(state)).find(e => e.isFetching && e.profile !== 'http://iiif.io/api/auth/1/external') || {}).id,
 });
 
 /**
diff --git a/src/containers/CanvasAnnotations.js b/src/containers/CanvasAnnotations.js
index 31245670d7138c6ba268de22d62e2d9e7c2c4c87..f44fc9f03319e00e01ece4a5a67f25f6a9c9a4ac 100644
--- a/src/containers/CanvasAnnotations.js
+++ b/src/containers/CanvasAnnotations.js
@@ -8,6 +8,7 @@ import {
   getAnnotationResourcesByMotivationForCanvas,
   getCanvasLabel,
   getSelectedAnnotationId,
+  getConfig,
 } from '../state/selectors';
 import { CanvasAnnotations } from '../components/CanvasAnnotations';
 
@@ -28,10 +29,10 @@ function getIdAndContentOfResources(resources) {
 const mapStateToProps = (state, { canvasId, windowId }) => ({
   annotations: getIdAndContentOfResources(
     getAnnotationResourcesByMotivationForCanvas(
-      state, { canvasId, motivations: state.config.annotations.filteredMotivations, windowId },
+      state, { canvasId, windowId },
     ),
   ),
-  htmlSanitizationRuleSet: state.config.annotations.htmlSanitizationRuleSet,
+  htmlSanitizationRuleSet: getConfig(state).annotations.htmlSanitizationRuleSet,
   label: getCanvasLabel(state, {
     canvasId,
     windowId,
diff --git a/src/containers/ChangeThemeDialog.js b/src/containers/ChangeThemeDialog.js
index b992922c008b67f2445c23443d197414b4fe32d9..ade8c4eeebe49ebeb76b720f099b1ec4be4863c6 100644
--- a/src/containers/ChangeThemeDialog.js
+++ b/src/containers/ChangeThemeDialog.js
@@ -4,7 +4,7 @@ import { withStyles } from '@material-ui/core';
 import { withTranslation } from 'react-i18next';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
-import { getThemeIds } from '../state/selectors';
+import { getThemeIds, getConfig } from '../state/selectors';
 import { ChangeThemeDialog } from '../components/ChangeThemeDialog';
 
 /**
@@ -22,7 +22,7 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({
  * @private
  */
 const mapStateToProps = state => ({
-  selectedTheme: state.config.selectedTheme,
+  selectedTheme: getConfig(state).selectedTheme,
   themeIds: getThemeIds(state),
 });
 
diff --git a/src/containers/ErrorContent.js b/src/containers/ErrorContent.js
index c6b864cc280156ac2abbef5500907af54e1a247d..951d3e20158177d5b3a148e163da9e513b448839 100644
--- a/src/containers/ErrorContent.js
+++ b/src/containers/ErrorContent.js
@@ -9,6 +9,7 @@ import {
   getManifest,
   getWindow,
   getViewer,
+  getConfig,
 } from '../state/selectors';
 
 /** mapStateToProps */
@@ -19,7 +20,7 @@ const mapStateToProps = (state, { companionWindowId, windowId }) => ({
     viewer: getViewer(state, { windowId }),
     window: getWindow(state, { windowId }),
   },
-  showJsError: state.config.window.showJsError,
+  showJsError: getConfig(state).window.showJsError,
 });
 
 /**
diff --git a/src/containers/ErrorDialog.js b/src/containers/ErrorDialog.js
index 48fbea86332541a0635a0dc33bdc2625a07a32cd..9aeaaa578fab376af5b5a6095c493be608d760b5 100644
--- a/src/containers/ErrorDialog.js
+++ b/src/containers/ErrorDialog.js
@@ -1,14 +1,10 @@
 import { compose } from 'redux';
 import { connect } from 'react-redux';
 import { withTranslation } from 'react-i18next';
-import {
-  first,
-  omit,
-  values,
-} from 'lodash';
 import { withPlugins } from '../extend/withPlugins';
 import { ErrorDialog } from '../components/ErrorDialog';
 import * as actions from '../state/actions';
+import { getLatestError } from '../state/selectors';
 
 /**
  * mapStateToProps - to hook up connect
@@ -16,8 +12,7 @@ import * as actions from '../state/actions';
  * @private
  */
 const mapStateToProps = state => ({
-  /* extract 'items' value and get first key-value-pair (an error) */
-  error: first(values(omit(state.errors, 'items'))),
+  error: getLatestError(state),
 });
 
 /**
diff --git a/src/containers/GalleryViewThumbnail.js b/src/containers/GalleryViewThumbnail.js
index 268bc7e316b4e4e3826556eb83101dfcea399f00..ade22c819931935bc40c4f0e1fa93d59cbeb1c8a 100644
--- a/src/containers/GalleryViewThumbnail.js
+++ b/src/containers/GalleryViewThumbnail.js
@@ -8,6 +8,7 @@ import {
   getSearchAnnotationsForWindow,
   getSelectedContentSearchAnnotationIds,
   getCurrentCanvas,
+  getConfig,
 } from '../state/selectors';
 
 /**
@@ -70,7 +71,7 @@ const mapStateToProps = (state, { canvas, windowId }) => {
   return {
     annotationsCount: canvasAnnotations.length,
     annotationSelected: canvasAnnotations.some(a => selectedAnnotationIds.includes(a.id)),
-    config: state.config.galleryView,
+    config: getConfig(state).galleryView,
     selected: currentCanvas && currentCanvas.id === canvas.id,
   };
 };
diff --git a/src/containers/MinimalWindow.js b/src/containers/MinimalWindow.js
index 20351e144ade292a3c697df3e71516433cf0abe1..b0e4974bcbd8e50c8758b70a83c568291cac5f3a 100644
--- a/src/containers/MinimalWindow.js
+++ b/src/containers/MinimalWindow.js
@@ -5,11 +5,12 @@ import { withStyles } from '@material-ui/core/styles';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
 import { MinimalWindow } from '../components/MinimalWindow';
+import { getWindowConfig } from '../state/selectors';
 
 /** mapStateToProps */
 const mapStateToProps = (state, { windowId }) => ({
-  allowClose: state.config.window.allowClose,
-  allowWindowSideBar: state.config.window.allowWindowSideBar,
+  allowClose: getWindowConfig(state, { windowId }).allowClose,
+  allowWindowSideBar: getWindowConfig(state, { windowId }).allowWindowSideBar,
 });
 
 /**
diff --git a/src/containers/OpenSeadragonViewer.js b/src/containers/OpenSeadragonViewer.js
index 25bd53f6f5b91070af23e2c9f642ec9733dc4e05..7b3fc142dc077d905fe401ab3f2b44df915f14a0 100644
--- a/src/containers/OpenSeadragonViewer.js
+++ b/src/containers/OpenSeadragonViewer.js
@@ -43,7 +43,7 @@ const mapStateToProps = (state, { windowId }) => {
         && infoResponse.isFetching === false
         && infoResponse.error === undefined)),
     nonTiledImages: getVisibleCanvasNonTiledResources(state, { windowId }),
-    osdConfig: state.config.osdConfig,
+    osdConfig: getConfig(state).osdConfig,
     viewerConfig: getViewer(state, { windowId }),
   };
 };
diff --git a/src/containers/SidebarIndexThumbnail.js b/src/containers/SidebarIndexThumbnail.js
index f357f5e040e6aa4c7eca0ce4139c31f9f37eb6c8..9f582dc4647693cfb9d78d574648bd4169f066d1 100644
--- a/src/containers/SidebarIndexThumbnail.js
+++ b/src/containers/SidebarIndexThumbnail.js
@@ -4,6 +4,7 @@ import { withTranslation } from 'react-i18next';
 import { withStyles } from '@material-ui/core/styles';
 import { withPlugins } from '../extend/withPlugins';
 import { SidebarIndexThumbnail } from '../components/SidebarIndexThumbnail';
+import { getConfig } from '../state/selectors';
 
 /**
  * mapStateToProps - used to hook up state to props
@@ -11,7 +12,7 @@ import { SidebarIndexThumbnail } from '../components/SidebarIndexThumbnail';
  * @private
  */
 const mapStateToProps = (state, { data }) => ({
-  config: state.config,
+  ...(getConfig(state).canvasNavigation || {}),
 });
 
 /**
diff --git a/src/containers/ThumbnailNavigation.js b/src/containers/ThumbnailNavigation.js
index df0e69e15228cd613c99d8e61eec331a62759d6a..eb82793df90cd4a35c6432dfe4b9a59d339bd6cb 100644
--- a/src/containers/ThumbnailNavigation.js
+++ b/src/containers/ThumbnailNavigation.js
@@ -7,9 +7,10 @@ import CanvasGroupings from '../lib/CanvasGroupings';
 import * as actions from '../state/actions';
 import { ThumbnailNavigation } from '../components/ThumbnailNavigation';
 import {
+  getCompanionWindow, getWindow,
   getNextCanvasGrouping, getPreviousCanvasGrouping,
   getCanvases, getCanvasIndex, getWindowViewType,
-  getSequenceViewingDirection,
+  getSequenceViewingDirection, getConfig,
 } from '../state/selectors';
 
 /**
@@ -27,8 +28,10 @@ const mapStateToProps = (state, { windowId }) => {
     canvasIndex: getCanvasIndex(state, { windowId }),
     hasNextCanvas: !!getNextCanvasGrouping(state, { windowId }),
     hasPreviousCanvas: !!getPreviousCanvasGrouping(state, { windowId }),
-    position: state.companionWindows[state.windows[windowId].thumbnailNavigationId].position,
-    thumbnailNavigation: state.config.thumbnailNavigation,
+    position: getCompanionWindow(state, {
+      companionWindowId: getWindow(state, { windowId }).thumbnailNavigationId,
+    }).position,
+    thumbnailNavigation: getConfig(state).thumbnailNavigation,
     view: viewType,
     viewingDirection: getSequenceViewingDirection(state, { windowId }),
   };
diff --git a/src/containers/WindowCanvasNavigationControls.js b/src/containers/WindowCanvasNavigationControls.js
index 0f8bf427eb580913276b3a7898955451d0fda169..7fc436c929e8d50381518c2b1be502de884e001e 100644
--- a/src/containers/WindowCanvasNavigationControls.js
+++ b/src/containers/WindowCanvasNavigationControls.js
@@ -7,6 +7,7 @@ import { withPlugins } from '../extend/withPlugins';
 import {
   getCurrentCanvas,
   getCanvasLabel,
+  getWorkspace,
 } from '../state/selectors';
 import { WindowCanvasNavigationControls } from '../components/WindowCanvasNavigationControls';
 
@@ -16,7 +17,7 @@ const mapStateToProps = (state, { windowId }) => ({
     canvasId: (getCurrentCanvas(state, { windowId }) || {}).id,
     windowId,
   }),
-  visible: state.workspace.focusedWindowId === windowId,
+  visible: getWorkspace(state).focusedWindowId === windowId,
 });
 
 /**
diff --git a/src/containers/WindowList.js b/src/containers/WindowList.js
index 2e76904ed3780c4aff4eca6c07c811a09ed9aa78..0d8f32c4d4d0394eca87db97c00ca0528a626384 100644
--- a/src/containers/WindowList.js
+++ b/src/containers/WindowList.js
@@ -3,7 +3,7 @@ import { compose } from 'redux';
 import { withTranslation } from 'react-i18next';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
-import { getContainerId, getWindowTitles } from '../state/selectors';
+import { getContainerId, getWindowIds, getWindowTitles } from '../state/selectors';
 import { WindowList } from '../components/WindowList';
 
 /**
@@ -24,7 +24,7 @@ const mapStateToProps = state => (
   {
     containerId: getContainerId(state),
     titles: getWindowTitles(state),
-    windowIds: Object.keys(state.windows),
+    windowIds: getWindowIds(state),
   }
 );
 
diff --git a/src/containers/WindowListButton.js b/src/containers/WindowListButton.js
index f35f162ca27446079efe1a5260d23ff9284552a0..74506d9dbaec7e421ae6288d6eb93e64210aeaec 100644
--- a/src/containers/WindowListButton.js
+++ b/src/containers/WindowListButton.js
@@ -3,12 +3,13 @@ import { connect } from 'react-redux';
 import { withTranslation } from 'react-i18next';
 import { withStyles } from '@material-ui/core/styles';
 import { withPlugins } from '../extend/withPlugins';
+import { getWindowIds, getWorkspace } from '../state/selectors';
 import { WindowListButton } from '../components/WindowListButton';
 
 /** */
-const mapStateToProps = ({ windows, workspace }) => ({
-  disabled: workspace.isWorkspaceAddVisible,
-  windowCount: Object.keys(windows).length,
+const mapStateToProps = (state) => ({
+  disabled: getWorkspace(state).isWorkspaceAddVisible,
+  windowCount: getWindowIds(state).length,
 });
 
 /**
diff --git a/src/containers/WindowSideBarAnnotationsPanel.js b/src/containers/WindowSideBarAnnotationsPanel.js
index 64af2caedc2d7bf2977b00037c2296865eefb012..854f5175882ed828f177d762ef796abd6a4f7bab 100644
--- a/src/containers/WindowSideBarAnnotationsPanel.js
+++ b/src/containers/WindowSideBarAnnotationsPanel.js
@@ -17,7 +17,7 @@ import { WindowSideBarAnnotationsPanel } from '../components/WindowSideBarAnnota
 const mapStateToProps = (state, { windowId }) => ({
   annotationCount: getAnnotationResourcesByMotivation(
     state,
-    { motivations: state.config.annotations.filteredMotivations, windowId },
+    { windowId },
   ).length,
   selectedCanvases: getVisibleCanvases(state, { windowId }),
 });
diff --git a/src/containers/WindowSideBarButtons.js b/src/containers/WindowSideBarButtons.js
index 4b08c5f80429656f064da8aeda5925b0efebcb22..a8f70f97d1704ff54c9471d0fd0f9b5726e65aa8 100644
--- a/src/containers/WindowSideBarButtons.js
+++ b/src/containers/WindowSideBarButtons.js
@@ -41,7 +41,7 @@ function hasLayers(canvases) {
 const mapStateToProps = (state, { windowId }) => ({
   hasAnnotations: getAnnotationResourcesByMotivation(
     state,
-    { motivations: state.config.annotations.filteredMotivations, windowId },
+    { windowId },
   ).length > 0,
   hasAnyLayers: hasLayers(getCanvases(state, { windowId })),
   hasCurrentLayers: hasLayers(getVisibleCanvases(state, { windowId })),
diff --git a/src/containers/WindowSideBarInfoPanel.js b/src/containers/WindowSideBarInfoPanel.js
index 54652a14a1ee6748c39ab68ec777012227d420d0..812dfad47ee483978bb62c1ef08746fda2f01250 100644
--- a/src/containers/WindowSideBarInfoPanel.js
+++ b/src/containers/WindowSideBarInfoPanel.js
@@ -5,6 +5,7 @@ import { withStyles } from '@material-ui/core/styles';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
 import {
+  getCompanionWindow,
   getManifestLocale,
   getMetadataLocales,
   getVisibleCanvases,
@@ -19,7 +20,8 @@ import { WindowSideBarInfoPanel } from '../components/WindowSideBarInfoPanel';
  */
 const mapStateToProps = (state, { id, windowId }) => ({
   availableLocales: getMetadataLocales(state, { companionWindowId: id, windowId }),
-  locale: state.companionWindows[id].locale || getManifestLocale(state, { windowId }),
+  locale: getCompanionWindow(state, { companionWindowId: id }).locale
+    || getManifestLocale(state, { windowId }),
   selectedCanvases: getVisibleCanvases(state, { windowId }),
   showLocalePicker: getWindowConfig(state, { windowId }).showLocalePicker,
 });
diff --git a/src/containers/WindowTopBar.js b/src/containers/WindowTopBar.js
index bfb5ffdbeb48fbc9f87c351d4b01b7440d0a98f8..eebf13ebba6d2ceb07444f58b0937d207dd4d7d9 100644
--- a/src/containers/WindowTopBar.js
+++ b/src/containers/WindowTopBar.js
@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
 import { withStyles } from '@material-ui/core';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
-import { getWindowConfig } from '../state/selectors';
+import { getWindowConfig, isFocused } from '../state/selectors';
 import { WindowTopBar } from '../components/WindowTopBar';
 
 /** mapStateToProps */
@@ -17,7 +17,7 @@ const mapStateToProps = (state, { windowId }) => {
     allowMaximize: config.allowMaximize,
     allowTopMenuButton: config.allowTopMenuButton,
     allowWindowSideBar: config.allowWindowSideBar,
-    focused: state.workspace.focusedWindowId === windowId,
+    focused: isFocused(state, { windowId }),
     maximized: config.maximized,
   };
 };
diff --git a/src/containers/Workspace.js b/src/containers/Workspace.js
index 63737d8d15756d361d76e3af76a8fadb1d1359ad..6eac59013b39931e67ce89e965cb58bfa3678c7b 100644
--- a/src/containers/Workspace.js
+++ b/src/containers/Workspace.js
@@ -4,7 +4,10 @@ import { withTranslation } from 'react-i18next';
 import { withStyles } from '@material-ui/core/styles';
 import { withPlugins } from '../extend/withPlugins';
 import { Workspace } from '../components/Workspace';
-import { getMaximizedWindowsIds, getWindowIds, getWorkspaceType } from '../state/selectors';
+import {
+  getMaximizedWindowsIds, getWindowIds, getWorkspaceType,
+  getConfig, getWorkspace,
+} from '../state/selectors';
 import * as actions from '../state/actions';
 
 /**
@@ -14,11 +17,11 @@ import * as actions from '../state/actions';
  */
 const mapStateToProps = state => (
   {
-    allowNewWindows: state.config.workspace.allowNewWindows,
-    isWorkspaceControlPanelVisible: state.config.workspaceControlPanel.enabled,
+    allowNewWindows: getConfig(state).workspace.allowNewWindows,
+    isWorkspaceControlPanelVisible: getConfig(state).workspaceControlPanel.enabled,
     maximizedWindowIds: getMaximizedWindowsIds(state),
     windowIds: getWindowIds(state),
-    workspaceId: state.workspace.id,
+    workspaceId: getWorkspace(state).id,
     workspaceType: getWorkspaceType(state),
   }
 );
diff --git a/src/containers/WorkspaceAdd.js b/src/containers/WorkspaceAdd.js
index 9fd38e4023581ce98bf10f1e70309e8bbff0fa66..fe7b6c68232c39a5a2e9a4f7e815cf198335f288 100644
--- a/src/containers/WorkspaceAdd.js
+++ b/src/containers/WorkspaceAdd.js
@@ -5,13 +5,14 @@ import { withStyles } from '@material-ui/core';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
 import { WorkspaceAdd } from '../components/WorkspaceAdd';
+import { getCatalog } from '../state/selectors';
 
 /**
  * mapStateToProps - to hook up connect
  * @memberof Workspace
  * @private
  */
-const mapStateToProps = state => ({ catalog: state.catalog });
+const mapStateToProps = state => ({ catalog: getCatalog(state) });
 
 /**
  * mapDispatchToProps - used to hook up connect to action creators
diff --git a/src/containers/WorkspaceAddButton.js b/src/containers/WorkspaceAddButton.js
index 4635e30a04227fcf912f3fd3d97479853c1d1464..642e83fc482c9fbcf3404c6d35fb4701e4d48c10 100644
--- a/src/containers/WorkspaceAddButton.js
+++ b/src/containers/WorkspaceAddButton.js
@@ -5,6 +5,7 @@ import { withStyles } from '@material-ui/core';
 import withWidth from '@material-ui/core/withWidth';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
+import { getWindowIds, getWorkspace } from '../state/selectors';
 import { WorkspaceAddButton } from '../components/WorkspaceAddButton';
 
 /**
@@ -13,13 +14,13 @@ import { WorkspaceAddButton } from '../components/WorkspaceAddButton';
  * @private
  */
 const mapStateToProps = (state, { width }) => {
-  const { isWorkspaceAddVisible } = state.workspace;
+  const { isWorkspaceAddVisible } = getWorkspace(state);
   return {
     isWorkspaceAddVisible,
     useExtendedFab: (
       (width !== 'xs')
         && !isWorkspaceAddVisible
-        && Object.keys(state.windows).length === 0
+        && getWindowIds(state).length === 0
     ),
   };
 };
diff --git a/src/containers/WorkspaceElasticWindow.js b/src/containers/WorkspaceElasticWindow.js
index 8da97f482f2f181def882dfecdc71584e723ee3f..e619b40e388e61baa51c2565978aa8326739a08c 100644
--- a/src/containers/WorkspaceElasticWindow.js
+++ b/src/containers/WorkspaceElasticWindow.js
@@ -3,7 +3,10 @@ import { connect } from 'react-redux';
 import { withStyles } from '@material-ui/core';
 import * as actions from '../state/actions';
 import WorkspaceElasticWindow from '../components/WorkspaceElasticWindow';
-import { selectCompanionWindowDimensions } from '../state/selectors';
+import {
+  selectCompanionWindowDimensions, getWorkspace, isFocused,
+  getElasticLayout,
+} from '../state/selectors';
 
 /**
  * mapStateToProps - to hook up connect
@@ -13,9 +16,9 @@ import { selectCompanionWindowDimensions } from '../state/selectors';
 const mapStateToProps = (state, { windowId }) => (
   {
     companionWindowDimensions: selectCompanionWindowDimensions(state, { windowId }),
-    focused: state.workspace.focusedWindowId === windowId,
-    layout: state.elasticLayout[windowId],
-    workspace: state.workspace,
+    focused: isFocused(state, { windowId }),
+    layout: getElasticLayout(state)[windowId],
+    workspace: getWorkspace(state),
   }
 );
 
diff --git a/src/containers/WorkspaceMenu.js b/src/containers/WorkspaceMenu.js
index dcf19e1e512f7063ba27b0e81ecbe1fc53206f54..afc303cfc921a212d52c8d5b9bcbc75b5a9b6398 100644
--- a/src/containers/WorkspaceMenu.js
+++ b/src/containers/WorkspaceMenu.js
@@ -3,7 +3,10 @@ import { connect } from 'react-redux';
 import { withTranslation } from 'react-i18next';
 import { withPlugins } from '../extend/withPlugins';
 import * as actions from '../state/actions';
-import { getContainerId, getShowZoomControlsConfig, getThemeIds } from '../state/selectors';
+import {
+  getContainerId, getShowZoomControlsConfig, getThemeIds,
+  getWorkspace,
+} from '../state/selectors';
 import { WorkspaceMenu } from '../components/WorkspaceMenu';
 
 /**
@@ -22,7 +25,7 @@ const mapDispatchToProps = {
  */
 const mapStateToProps = state => ({
   containerId: getContainerId(state),
-  isWorkspaceAddVisible: state.workspace.isWorkspaceAddVisible,
+  isWorkspaceAddVisible: getWorkspace(state).isWorkspaceAddVisible,
   showThemePicker: getThemeIds(state).length > 0,
   showZoomControls: getShowZoomControlsConfig(state),
 });
diff --git a/src/containers/WorkspaceMosaic.js b/src/containers/WorkspaceMosaic.js
index 3afb145675dbadb48ab415e2de267ded1e2406b1..46ad650e49c1f2d45194f1df837a21812e34f1db 100644
--- a/src/containers/WorkspaceMosaic.js
+++ b/src/containers/WorkspaceMosaic.js
@@ -2,6 +2,7 @@ import { compose } from 'redux';
 import { connect } from 'react-redux';
 import { withStyles } from '@material-ui/core/styles';
 import { withPlugins } from '../extend/withPlugins';
+import { getWorkspace } from '../state/selectors';
 import * as actions from '../state/actions';
 import { WorkspaceMosaic } from '../components/WorkspaceMosaic';
 
@@ -12,9 +13,9 @@ import { WorkspaceMosaic } from '../components/WorkspaceMosaic';
  */
 const mapStateToProps = state => (
   {
-    layout: state.workspace.layout,
-    windows: state.windows,
-    workspaceId: state.workspace.id,
+    layout: getWorkspace(state).layout,
+    windowIds: getWorkspace(state).windowIds,
+    workspaceId: getWorkspace(state).id,
   }
 );
 
diff --git a/src/state/actions/window.js b/src/state/actions/window.js
index 7880dbace709124c7c4a5e85063f428b4b70e1ec..6ba1a236967c8d934b0af8718e0497cbca89e281 100644
--- a/src/state/actions/window.js
+++ b/src/state/actions/window.js
@@ -1,5 +1,6 @@
 import { v4 as uuid } from 'uuid';
 import ActionTypes from './action-types';
+import { miradorSlice } from '../selectors/utils';
 
 /**
  * focusWindow - action creator
@@ -23,8 +24,8 @@ export function focusWindow(windowId, pan = false) {
  */
 export function addWindow({ companionWindows, manifest, ...options }) {
   return (dispatch, getState) => {
-    const { config, windows } = getState();
-    const numWindows = Object.keys(windows).length;
+    const { config, workspace: { windowIds = [] } } = miradorSlice(getState());
+    const numWindows = windowIds.length;
 
     const windowId = options.id || `window-${uuid()}`;
     const cwThumbs = `cw-${uuid()}`;
@@ -63,7 +64,6 @@ export function addWindow({ companionWindows, manifest, ...options }) {
       draggingEnabled: true,
       highlightAllAnnotations: config.window.highlightAllAnnotations || false,
       id: windowId,
-      layoutOrder: numWindows + 1,
       manifestId: null,
       maximized: false,
       rangeId: null,
diff --git a/src/state/createStore.js b/src/state/createStore.js
index b4c76bda46f210a10c85fb6b22a95aad21db1fa2..158f359708c1811f8c09600349302e13863b6408 100644
--- a/src/state/createStore.js
+++ b/src/state/createStore.js
@@ -5,10 +5,11 @@
 
 import thunkMiddleware from 'redux-thunk';
 import createSagaMiddleware from 'redux-saga';
-import { createStore, applyMiddleware } from 'redux';
+import { combineReducers, createStore, applyMiddleware } from 'redux';
 import { composeWithDevTools } from 'redux-devtools-extension';
 import createRootReducer from './reducers/rootReducer';
 import getRootSaga from './sagas';
+import settings from '../config/settings';
 
 // create the saga middleware
 const sagaMiddleware = createSagaMiddleware();
@@ -17,8 +18,14 @@ const sagaMiddleware = createSagaMiddleware();
  * Configure Store
  */
 export default function (pluginReducers, pluginSagas = []) {
+  const miradorReducer = createRootReducer(pluginReducers);
+
+  const rootReducer = settings.state.slice
+    ? combineReducers({ [settings.state.slice]: miradorReducer })
+    : miradorReducer;
+
   const store = createStore(
-    createRootReducer(pluginReducers),
+    rootReducer,
     composeWithDevTools(
       applyMiddleware(
         thunkMiddleware, sagaMiddleware,
diff --git a/src/state/sagas/app.js b/src/state/sagas/app.js
index de44905423376b3cc3b5a212f2d502fe34e2a981..ca8bd44d6027775407d475f75cafa78a3c8a6ad6 100644
--- a/src/state/sagas/app.js
+++ b/src/state/sagas/app.js
@@ -23,14 +23,13 @@ export function* importConfig({ config: { thumbnailNavigation, windows } }) {
   if (!windows || windows.length === 0) return;
 
   const thunks = yield all(
-    windows.map((miradorWindow, layoutOrder) => {
+    windows.map((miradorWindow) => {
       const windowId = `window-${uuid()}`;
       const manifestId = miradorWindow.manifestId || miradorWindow.loadedManifest;
 
       return call(addWindow, {
         // these are default values ...
         id: windowId,
-        layoutOrder,
         manifestId,
         thumbnailNavigationPosition: thumbnailNavigation && thumbnailNavigation.defaultPosition,
         // ... overridden by values from the window configuration ...
diff --git a/src/state/selectors/annotations.js b/src/state/selectors/annotations.js
index d8849b6df12b5d1256aef424ac1b4b4a4b9ee20c..6a86baa4faac3c936e9eb683ebef1f62feff26c2 100644
--- a/src/state/selectors/annotations.js
+++ b/src/state/selectors/annotations.js
@@ -2,10 +2,21 @@ import { createSelector } from 'reselect';
 import filter from 'lodash/filter';
 import flatten from 'lodash/flatten';
 import AnnotationFactory from '../../lib/AnnotationFactory';
+import { miradorSlice } from './utils';
 import { getCanvas, getVisibleCanvasIds } from './canvases';
+import { getConfig } from './config';
+import { getWindow } from './getters';
 
 /** */
-export const getAnnotations = state => state.annotations;
+export const getAnnotations = state => miradorSlice(state).annotations;
+
+const getMotiviation = createSelector(
+  [
+    getConfig,
+    (state, { motivations }) => motivations,
+  ],
+  (config, motivations) => motivations || config.annotations.filteredMotivations,
+);
 
 const getAnnotationsOnCanvas = createSelector(
   [
@@ -34,7 +45,7 @@ const getPresentAnnotationsCanvas = createSelector(
 const getAnnotationsOnSelectedCanvases = createSelector(
   [
     getVisibleCanvasIds,
-    state => state.annotations,
+    getAnnotations,
   ],
   (canvasIds, annotations) => {
     if (!annotations || canvasIds.length === 0) return [];
@@ -66,7 +77,7 @@ export const getPresentAnnotationsOnSelectedCanvases = createSelector(
 export const getAnnotationResourcesByMotivationForCanvas = createSelector(
   [
     getPresentAnnotationsCanvas,
-    (state, { motivations }) => motivations,
+    getMotiviation,
   ],
   (annotations, motivations) => filter(
     flatten(annotations.map(annotation => annotation.resources)),
@@ -85,7 +96,7 @@ export const getAnnotationResourcesByMotivationForCanvas = createSelector(
 export const getAnnotationResourcesByMotivation = createSelector(
   [
     getPresentAnnotationsOnSelectedCanvases,
-    (state, { motivations }) => motivations,
+    getMotiviation,
   ],
   (annotations, motivations) => filter(
     flatten(annotations.map(annotation => annotation.resources)),
@@ -104,9 +115,9 @@ export const getAnnotationResourcesByMotivation = createSelector(
  */
 export const getSelectedAnnotationId = createSelector(
   [
-    (state, { windowId }) => state.windows[windowId].selectedAnnotationId,
+    getWindow,
   ],
-  selectedAnnotationId => selectedAnnotationId,
+  ({ selectedAnnotationId }) => selectedAnnotationId,
 );
 
 export const getSelectedAnnotationsOnCanvases = createSelector(
diff --git a/src/state/selectors/auth.js b/src/state/selectors/auth.js
index abe9ca55da6482db8bcec08d57e372eaa596f3ee..c1d75341d9e3bc86a3d02aef9c074f24730b3964 100644
--- a/src/state/selectors/auth.js
+++ b/src/state/selectors/auth.js
@@ -1,2 +1,7 @@
+import { miradorSlice } from './utils';
+
 /** */
-export const getAccessTokens = state => state.accessTokens;
+export const getAccessTokens = state => miradorSlice(state).accessTokens || {};
+
+/** */
+export const getAuth = state => miradorSlice(state).auth || {};
diff --git a/src/state/selectors/canvases.js b/src/state/selectors/canvases.js
index 2250708332ec21d318bbebdb2eb2814187a9b540..d2083c184f89a5e46e2ec4afb0633ef399ec2b2b 100644
--- a/src/state/selectors/canvases.js
+++ b/src/state/selectors/canvases.js
@@ -3,10 +3,14 @@ import { Utils } from 'manifesto.js/dist-esmodule/Utils';
 import flatten from 'lodash/flatten';
 import CanvasGroupings from '../../lib/CanvasGroupings';
 import MiradorCanvas from '../../lib/MiradorCanvas';
+import { miradorSlice } from './utils';
 import { getWindow } from './getters';
 import { getSequence } from './sequences';
 import { getWindowViewType } from './windows';
 
+/** */
+export const selectInfoResponses = state => miradorSlice(state).infoResponses;
+
 export const getCanvases = createSelector(
   [getSequence],
   sequence => (sequence && sequence.getCanvases()) || [],
@@ -172,9 +176,6 @@ export const getCanvasDescription = createSelector(
   canvas => canvas && canvas.getProperty('description'),
 );
 
-/** */
-export const selectInfoResponses = state => state.infoResponses;
-
 export const getVisibleCanvasNonTiledResources = createSelector(
   [
     getVisibleCanvases,
diff --git a/src/state/selectors/companionWindows.js b/src/state/selectors/companionWindows.js
index 692ee92295682ff930f857846055bbda0370c6ea..914c73a2f0148bd3d4e285e558d9397cd234332b 100644
--- a/src/state/selectors/companionWindows.js
+++ b/src/state/selectors/companionWindows.js
@@ -1,12 +1,21 @@
 import { createSelector } from 'reselect';
 import groupBy from 'lodash/groupBy';
+import { miradorSlice } from './utils';
 import { getWindow, getWindows } from './getters';
 
 /** */
 export function getCompanionWindows(state) {
-  return state.companionWindows;
+  return miradorSlice(state).companionWindows || {};
 }
 
+export const getCompanionWindow = createSelector(
+  [
+    getCompanionWindows,
+    (state, { companionWindowId }) => companionWindowId,
+  ],
+  (companionWindows, companionWindowId) => companionWindowId && companionWindows[companionWindowId],
+);
+
 /** Return position of thumbnail navigation in a certain window.
 * @param {object} state
 * @param {String} windowId
@@ -15,7 +24,7 @@ export function getCompanionWindows(state) {
 export const getThumbnailNavigationPosition = createSelector(
   [
     getWindow,
-    state => state.companionWindows,
+    getCompanionWindows,
   ],
   (window, companionWindows) => window
     && companionWindows[window.thumbnailNavigationId]
@@ -59,14 +68,6 @@ const getCompanionWindowsByWindowAndPosition = createSelector(
   ),
 );
 
-export const getCompanionWindow = createSelector(
-  [
-    getCompanionWindows,
-    (state, { companionWindowId }) => companionWindowId,
-  ],
-  (companionWindows, companionWindowId) => companionWindows[companionWindowId],
-);
-
 /**
  * Return companion windows of a window
  * @param {String} windowId
diff --git a/src/state/selectors/config.js b/src/state/selectors/config.js
index 29c7e3efe759040135d6860dd2e78071a9be694d..63ac28467fec86c6550b5c1e2eb3c7dea7515bbd 100644
--- a/src/state/selectors/config.js
+++ b/src/state/selectors/config.js
@@ -1,9 +1,12 @@
 import { createSelector } from 'reselect';
 import deepmerge from 'deepmerge';
+import { miradorSlice } from './utils';
+import { getWorkspace } from './getters';
 
 /** */
 export function getConfig(state) {
-  return (state && state.config) || {};
+  const slice = miradorSlice(state || {});
+  return slice.config || {};
 }
 
 /**
@@ -49,7 +52,7 @@ export const getLanguagesFromConfigWithCurrent = createSelector(
 
 export const getShowZoomControlsConfig = createSelector(
   [
-    state => state.workspace,
+    getWorkspace,
     getConfig,
   ],
   (workspace, config) => (
diff --git a/src/state/selectors/getters.js b/src/state/selectors/getters.js
index b8db261fbb61f732188bacf8eee40e315aa4f36a..066dce47da749c50eaa42e10ae5e619a9f3b8d19 100644
--- a/src/state/selectors/getters.js
+++ b/src/state/selectors/getters.js
@@ -1,4 +1,5 @@
 import { createSelector } from 'reselect';
+import { miradorSlice } from './utils';
 
 /**
  * Return the manifest titles for all open windows
@@ -6,12 +7,12 @@ import { createSelector } from 'reselect';
  * @return {object}
  */
 export function getWindowManifests(state) {
-  return Object.values(state.windows).map(window => window.manifestId);
+  return Object.values(miradorSlice(state).windows).map(window => window.manifestId);
 }
 
 /** */
 export function getWindows(state) {
-  return state.windows || {};
+  return miradorSlice(state).windows || {};
 }
 
 /** */
@@ -21,21 +22,26 @@ export function getWindow(state, { windowId }) {
 
 export const getViewer = createSelector(
   [
-    state => state.viewers,
+    state => miradorSlice(state).viewers,
     (state, { windowId }) => windowId,
   ],
   (viewers, windowId) => viewers[windowId],
 );
 
+/** */
+export function getWorkspace(state) {
+  return miradorSlice(state).workspace;
+}
+
 /** */
 export const getWindowIds = createSelector(
-  [getWindows],
-  windows => Object.keys(windows),
+  [getWorkspace],
+  ({ windowIds }) => windowIds || [],
 );
 
 /** */
 export function getManifests(state) {
-  return state.manifests || {};
+  return miradorSlice(state).manifests || {};
 }
 
 /** Get the relevant manifest information */
@@ -46,3 +52,8 @@ export function getManifest(state, { manifestId, windowId }) {
     || (windowId && (getWindow(state, { windowId }) || {}).manifestId)
   ];
 }
+
+/** */
+export function getCatalog(state) {
+  return miradorSlice(state).catalog || {};
+}
diff --git a/src/state/selectors/index.js b/src/state/selectors/index.js
index a25c81f7eaee83eada4b8aa9e0e2f1e7aadb2773..866141957ffc7875f157663ae457c88428e38c5b 100644
--- a/src/state/selectors/index.js
+++ b/src/state/selectors/index.js
@@ -11,3 +11,4 @@ export * from './ranges';
 export * from './layers';
 export * from './sequences';
 export * from './auth';
+export * from './utils';
diff --git a/src/state/selectors/layers.js b/src/state/selectors/layers.js
index c1c3913bfd1a16dd01b3c12e68915f2050b8d739..e75c165b7e2c195c9153d2c87f030b38b72ed127 100644
--- a/src/state/selectors/layers.js
+++ b/src/state/selectors/layers.js
@@ -1,6 +1,7 @@
 import { createSelector } from 'reselect';
 import MiradorCanvas from '../../lib/MiradorCanvas';
 import { getCanvas, getVisibleCanvasIds } from './canvases';
+import { miradorSlice } from './utils';
 
 /**
  * Get the image layers from a canvas
@@ -21,7 +22,7 @@ export const getCanvasLayers = createSelector(
  */
 export const getLayers = createSelector(
   [
-    state => state.layers || {},
+    state => miradorSlice(state).layers || {},
     (state, { windowId }) => windowId,
     (state, { canvasId }) => canvasId,
   ],
diff --git a/src/state/selectors/manifests.js b/src/state/selectors/manifests.js
index 3c205cfce5a50d301bfd549dacf38e46cc9c343b..8b8d02bce111db0f52708a50d962f4dd2cb27407 100644
--- a/src/state/selectors/manifests.js
+++ b/src/state/selectors/manifests.js
@@ -3,7 +3,9 @@ import createCachedSelector from 're-reselect';
 import { LanguageMap } from 'manifesto.js/dist-esmodule/LanguageMap';
 import { Utils } from 'manifesto.js/dist-esmodule/Utils';
 import getThumbnail from '../../lib/ThumbnailFactory';
+import { getCompanionWindow } from './companionWindows';
 import { getManifest } from './getters';
+import { getConfig } from './config';
 
 /** */
 function createManifestoInstance(json, locale) {
@@ -11,6 +13,17 @@ function createManifestoInstance(json, locale) {
   return Utils.parseManifest(json, locale ? { locale } : undefined);
 }
 
+/** */
+const getLocale = createSelector(
+  [
+    getCompanionWindow,
+    getConfig,
+  ],
+  (companionWindow = {}, config = {}) => (
+    companionWindow.locale || config.language
+  ),
+);
+
 /** Convenience selector to get a manifest (or placeholder) */
 export const getManifestStatus = createSelector(
   [getManifest],
@@ -30,13 +43,10 @@ const getContextualManifestoInstance = createCachedSelector(
   (manifest, locale) => manifest
     && createManifestoInstance(manifest.json, locale),
 )(
-  (state, props) => [
-    props.manifestId,
-    props.windowId,
-    (state.companionWindows
-      && state.companionWindows[props.companionWindowId]
-      && state.companionWindows[props.companionWindowId].locale)
-      || (state.config && state.config.language),
+  (state, { companionWindowId, manifestId, windowId }) => [
+    manifestId,
+    windowId,
+    getLocale(state, { companionWindowId }),
   ].join(' - '), // Cache key consisting of manifestId, windowId, and locale
 );
 
@@ -63,13 +73,6 @@ function getProperty(property) {
   );
 }
 
-/** */
-function getLocale(state, { companionWindowId }) {
-  return (companionWindowId
-    && state.companionWindows[companionWindowId]
-    && state.companionWindows[companionWindowId].locale)
-    || (state.config && state.config.language);
-}
 /**
  * Get the logo for a manifest
  * @param {object} state
diff --git a/src/state/selectors/searches.js b/src/state/selectors/searches.js
index 97dd793c3a540e6ab67107aaad8cbc7603e8af2e..b65ad89351d353b987ec73bfdbcd81d0195ab8e0 100644
--- a/src/state/selectors/searches.js
+++ b/src/state/selectors/searches.js
@@ -5,11 +5,15 @@ import AnnotationList from '../../lib/AnnotationList';
 import { getCanvas, getCanvases } from './canvases';
 import { getWindow } from './getters';
 import { getManifestLocale } from './manifests';
+import { miradorSlice } from './utils';
+
+/** Get searches from state */
+const getSearches = (state) => miradorSlice(state).searches;
 
 export const getSearchForWindow = createSelector(
   [
     (state, { windowId }) => windowId,
-    state => state.searches,
+    getSearches,
   ],
   (windowId, searches) => {
     if (!windowId || !searches) return {};
diff --git a/src/state/selectors/utils.js b/src/state/selectors/utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..22de1e7913643e2dd12f2cf1637ae9cc9c79aa11
--- /dev/null
+++ b/src/state/selectors/utils.js
@@ -0,0 +1,8 @@
+import settings from '../../config/settings';
+
+/** */
+export function miradorSlice(state) {
+  if (settings.state.slice) return state[settings.state.slice];
+
+  return state;
+}
diff --git a/src/state/selectors/windows.js b/src/state/selectors/windows.js
index d379f514ba5f394088c277ba3e4c137d108a1424..d2a5434f1bedc640db336374df4eb76dd1457efa 100644
--- a/src/state/selectors/windows.js
+++ b/src/state/selectors/windows.js
@@ -3,14 +3,14 @@ import {
   getManifestTitle,
 } from './manifests';
 import { getConfig } from './config';
-import { getWindows, getWindow } from './getters';
+import { getWindows, getWindow, getWindowIds } from './getters';
 import { getWorkspaceType } from './workspace';
 import { getSequenceViewingHint, getSequenceBehaviors } from './sequences';
 
 /** */
 export const getWindowConfig = createSelector(
   [getConfig, getWindow],
-  ({ window: defaultConfig }, windowConfig) => ({ ...defaultConfig, ...windowConfig }),
+  ({ window: defaultConfig }, windowConfig = {}) => ({ ...defaultConfig, ...windowConfig }),
 );
 
 /**
@@ -92,7 +92,7 @@ export const getWindowDraggability = createSelector(
   [
     getWorkspaceType,
     getWindow,
-    state => Object.keys(state.windows).length > 1,
+    state => getWindowIds(state).length > 1,
   ],
   (workspaceType, window, manyWindows) => {
     if (workspaceType === 'elastic') return true;
diff --git a/src/state/selectors/workspace.js b/src/state/selectors/workspace.js
index ba152f79f3699a8e060eb823ad3f312ddb064499..69d8ff62d632dcc9fa1c128c21e3ca3c7bc7a9df 100644
--- a/src/state/selectors/workspace.js
+++ b/src/state/selectors/workspace.js
@@ -1,13 +1,10 @@
 import { createSelector } from 'reselect';
-
-/** */
-export function getWorkspace(state) {
-  return state.workspace;
-}
+import { getWorkspace } from './getters';
+import { miradorSlice } from './utils';
 
 /** */
 export function getElasticLayout(state) {
-  return state.elasticLayout;
+  return miradorSlice(state).elasticLayout;
 }
 
 export const getFullScreenEnabled = createSelector(
@@ -19,10 +16,22 @@ export const getFullScreenEnabled = createSelector(
  * @param {object} state
  */
 export function getLatestError(state) {
-  return state.errors.items[0] && state.errors[state.errors.items[0]];
+  const [errorId] = miradorSlice(state).errors.items;
+
+  return miradorSlice(state).errors[errorId];
 }
 
 export const getWorkspaceType = createSelector(
   [getWorkspace],
   ({ type }) => type,
 );
+
+const getFocusedWindowId = createSelector(
+  [getWorkspace],
+  ({ focusedWindowId }) => focusedWindowId,
+);
+
+/** Check if the current window is focused */
+export const isFocused = (state, { windowId }) => (
+  getFocusedWindowId(state) === windowId
+);