diff --git a/__tests__/integration/mirador/plugins/add.html b/__tests__/integration/mirador/plugins/add.html
index d50ca8046d8881c31f8962580a8613104e4b3781..6b20517e0539609fe1d4647cf0daa60b1cf62425 100644
--- a/__tests__/integration/mirador/plugins/add.html
+++ b/__tests__/integration/mirador/plugins/add.html
@@ -72,7 +72,7 @@
         mode: 'add',
         component: AddPluginComponentC,
       };
-      
+
       const wrapPluginD = {
         target: 'WindowTopBarPluginMenu',
         mode: 'wrap',
diff --git a/__tests__/integration/mirador/plugins/companionWindow.html b/__tests__/integration/mirador/plugins/companionWindow.html
new file mode 100644
index 0000000000000000000000000000000000000000..b87dfff5827b9d6b438cffad7e656e2a12da14f1
--- /dev/null
+++ b/__tests__/integration/mirador/plugins/companionWindow.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <meta name="theme-color" content="#000000">
+    <title>Mirador</title>
+  </head>
+  <body>
+    <div id="mirador" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;"></div>
+    <script src="../../../../node_modules/react/umd/react.development.js"></script>
+    <script src="../../../../node_modules/react-dom/umd/react-dom.development.js"></script>
+    <script>document.write("<script type='text/javascript' src='../../../../dist/mirador.min.js?v=" + Date.now() + "'><\/script>");</script>
+    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
+    <script type="text/babel">
+
+      const config = { id: 'mirador', windows: [{manifestId: 'https://purl.stanford.edu/hg676jb4964/iiif/manifest'}] };
+
+      const AddPluginComponentA = (props) => (
+        <div id="add-plugin-companion-window">
+          Plugin A
+        </div>
+      );
+
+      const AddPluginComponentB = ({TabButton, PluginComponents, dispatch, ...otherProps}) => (
+        <TabButton id="add-plugin-companion-window-button" {...otherProps} label="P" value="pluginComponentA" />
+      );
+
+      const addPluginA = {
+        target: 'CompanionWindowFactory',
+        companionWindowKey: 'pluginComponentA',
+        mode: 'add',
+        component: AddPluginComponentA,
+      };
+
+      const addPluginB = {
+        target: 'WindowSideBarButtons',
+        mode: 'add',
+        component: AddPluginComponentB,
+      };
+
+      const miradorInstance = Mirador.viewer(config, [addPluginA, addPluginB]);
+
+     </script>
+  </body>
+</html>
diff --git a/__tests__/integration/mirador/plugins/companionWindow.test.js b/__tests__/integration/mirador/plugins/companionWindow.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..003fd12f84cd5df120d1fca9e897c0307a66d4bd
--- /dev/null
+++ b/__tests__/integration/mirador/plugins/companionWindow.test.js
@@ -0,0 +1,19 @@
+
+describe('add plugins for companion windows', () => {
+  beforeAll(async () => {
+    await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/plugins/companionWindow.html');
+    await expect(page).toMatchElement('.mirador-viewer');
+    await page.waitFor(1000);
+  });
+
+  it('added a plugin to the window sidebar and companion window', async () => {
+    await expect(page).toClick('button[aria-label="Toggle sidebar"]');
+
+    await page.waitFor(1000);
+    await expect(page).toMatchElement('.mirador-companion-window-left.mirador-window-sidebar-info-panel');
+    await expect(page).toMatchElement('#add-plugin-companion-window-button');
+
+    await expect(page).toClick('#add-plugin-companion-window-button');
+    await expect(page).toMatchElement('#add-plugin-companion-window');
+  });
+});
diff --git a/__tests__/src/extend/pluginPreprocessing.test.js b/__tests__/src/extend/pluginPreprocessing.test.js
index d695c53c98d613841e2cec87fe46782c456ec079..41177a1516f8b706597adf61c06d6ac8c3d0b9b1 100644
--- a/__tests__/src/extend/pluginPreprocessing.test.js
+++ b/__tests__/src/extend/pluginPreprocessing.test.js
@@ -3,6 +3,7 @@ import {
   connectPluginsToStore,
   addPluginReducersToStore,
   createTargetToPluginMapping,
+  addPluginsToCompanionWindowsRegistry,
 } from '../../../src/extend/pluginPreprocessing';
 
 
@@ -108,6 +109,26 @@ describe('connectPluginsToStore', () => {
   });
 });
 
+describe('addPluginsToCompanionWindowsRegistry', () => {
+  it('adds plugin references to the companion window registry', () => {
+    /** */
+    const ComponentA = props => null;
+
+    const plugins = [
+      {
+        companionWindowKey: 'xyz',
+        component: ComponentA,
+        mode: 'add',
+        target: 'CompanionWindowFactory',
+      },
+    ];
+
+    const result = addPluginsToCompanionWindowsRegistry(plugins);
+
+    expect(result.xyz).toBe(ComponentA);
+  });
+});
+
 describe('addPluginReducersToStore', () => {
   const store = { replaceReducer: jest.fn() };
   const createRootReducer = jest.fn(pluginReducers => pluginReducers);
diff --git a/src/components/CompanionWindowFactory.js b/src/components/CompanionWindowFactory.js
index eecfcaaf41be4d9845c6d1cdc29e87389d378953..82516259ca08757a95e38e6e9beff1c11de103fd 100644
--- a/src/components/CompanionWindowFactory.js
+++ b/src/components/CompanionWindowFactory.js
@@ -1,13 +1,6 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
-import ThumbnailNavigation from '../containers/ThumbnailNavigation';
-import WindowSideBarAnnotationsPanel from '../containers/WindowSideBarAnnotationsPanel';
-import WindowSideBarInfoPanel from '../containers/WindowSideBarInfoPanel';
-import WindowSideBarCanvasPanel from '../containers/WindowSideBarCanvasPanel';
-import AttributionPanel from '../containers/AttributionPanel';
-import SearchPanel from '../containers/SearchPanel';
-import LayersPanel from '../containers/LayersPanel';
-import CustomPanel from '../containers/CustomPanel';
+import CompanionWindowRegistry from '../lib/CompanionWindowRegistry';
 
 /**
  * Render a companion window using the appropriate component for the content
@@ -16,27 +9,11 @@ export class CompanionWindowFactory extends Component {
   /** */
   render() {
     const { content, windowId, id } = this.props;
+    const type = CompanionWindowRegistry[content];
 
-    switch (content) {
-      case 'info':
-        return (<WindowSideBarInfoPanel id={id} windowId={windowId} />);
-      case 'canvas':
-        return (<WindowSideBarCanvasPanel id={id} windowId={windowId} />);
-      case 'annotations':
-        return <WindowSideBarAnnotationsPanel id={id} windowId={windowId} />;
-      case 'thumbnailNavigation':
-        return <ThumbnailNavigation id={id} windowId={windowId} />;
-      case 'attribution':
-        return <AttributionPanel id={id} windowId={windowId} />;
-      case 'search':
-        return <SearchPanel id={id} windowId={windowId} />;
-      case 'layers':
-        return <LayersPanel id={id} windowId={windowId} />;
-      case 'custom':
-        return <CustomPanel id={id} windowId={windowId} />;
-      default:
-        return (<></>);
-    }
+    if (!type) return <></>;
+
+    return React.createElement(type, { id, windowId });
   }
 }
 
diff --git a/src/components/WindowSideBarButtons.js b/src/components/WindowSideBarButtons.js
index fe992e3475f55bcc10156a515589602f136d1f8d..279461019eb0216599dcf169e2258be3eeacc511 100644
--- a/src/components/WindowSideBarButtons.js
+++ b/src/components/WindowSideBarButtons.js
@@ -10,6 +10,7 @@ import AnnotationIcon from '@material-ui/icons/CommentSharp';
 import AttributionIcon from '@material-ui/icons/CopyrightSharp';
 import LayersIcon from '@material-ui/icons/LayersSharp';
 import SearchIcon from '@material-ui/icons/SearchSharp';
+import { PluginHook } from './PluginHook';
 import CanvasIndexIcon from './icons/CanvasIndexIcon';
 import { keys, chars } from '../lib/KeyHelper';
 /**
@@ -63,6 +64,7 @@ export class WindowSideBarButtons extends Component {
   */
   handleChange(event, value) {
     const { addCompanionWindow } = this.props;
+
     const tab = event.target;
     this.selectTab(tab);
     addCompanionWindow(value);
@@ -138,6 +140,7 @@ export class WindowSideBarButtons extends Component {
       hasSearchResults,
       hasSearchService,
       panels,
+      PluginComponents,
       sideBarPanel,
       t,
     } = this.props;
@@ -217,6 +220,7 @@ export class WindowSideBarButtons extends Component {
             )}
           />
         )}
+        <PluginHook TabButton={TabButton} PluginComponents={PluginComponents} />
       </Tabs>
     );
   }
@@ -231,6 +235,7 @@ WindowSideBarButtons.propTypes = {
   hasSearchResults: PropTypes.bool,
   hasSearchService: PropTypes.bool,
   panels: PropTypes.arrayOf(PropTypes.bool),
+  PluginComponents: PropTypes.array, // eslint-disable-line react/forbid-prop-types
   sideBarPanel: PropTypes.string,
   t: PropTypes.func,
 };
@@ -243,6 +248,7 @@ WindowSideBarButtons.defaultProps = {
   hasSearchResults: false,
   hasSearchService: false,
   panels: [],
+  PluginComponents: null,
   sideBarPanel: 'closed',
   t: key => key,
 };
diff --git a/src/extend/PluginProvider.js b/src/extend/PluginProvider.js
index e3c62358d03606d9fd452d9f06813834eabea943..6d4f327b61cb6d25f968067e0939396c5587433b 100644
--- a/src/extend/PluginProvider.js
+++ b/src/extend/PluginProvider.js
@@ -7,6 +7,7 @@ import {
   addPluginReducersToStore,
   connectPluginsToStore,
   createTargetToPluginMapping,
+  addPluginsToCompanionWindowsRegistry,
 } from './pluginPreprocessing';
 
 /**  */
@@ -19,6 +20,7 @@ export default function PluginProvider(props) {
     const validPlugins = filterValidPlugins(plugins);
     const connectedPlugins = connectPluginsToStore(validPlugins);
     createRootReducer && addPluginReducersToStore(store, createRootReducer, validPlugins);
+    addPluginsToCompanionWindowsRegistry(connectedPlugins);
     setPluginMap(createTargetToPluginMapping(connectedPlugins));
   }, []);
 
diff --git a/src/extend/pluginPreprocessing.js b/src/extend/pluginPreprocessing.js
index 77ac16ad4bec31f98ceb5d4419328342055d029a..e8c16801bb89856903690ae0dcb74f550e3715d0 100644
--- a/src/extend/pluginPreprocessing.js
+++ b/src/extend/pluginPreprocessing.js
@@ -1,6 +1,7 @@
 import update from 'lodash/update';
 import { connect } from 'react-redux';
 import { validatePlugin } from './pluginValidation';
+import CompanionWindowRegistry from '../lib/CompanionWindowRegistry';
 
 /**
  * Returns a mapping from targets to plugins and modes
@@ -42,6 +43,15 @@ export function addPluginReducersToStore(store, createRootReducer, plugins) {
   store.replaceReducer(createRootReducer(pluginReducers));
 }
 
+/** */
+export function addPluginsToCompanionWindowsRegistry(plugins) {
+  plugins.filter(p => p.companionWindowKey).forEach((plugin) => {
+    CompanionWindowRegistry[plugin.companionWindowKey] = plugin.component;
+  });
+
+  return CompanionWindowRegistry;
+}
+
 /** */
 function splitPluginsByValidation(plugins) {
   const splittedPlugins = { invalidPlugins: [], validPlugins: [] };
diff --git a/src/lib/CompanionWindowRegistry.js b/src/lib/CompanionWindowRegistry.js
new file mode 100644
index 0000000000000000000000000000000000000000..5197a0af1c54aa99279320e960ccd7e5abeb1381
--- /dev/null
+++ b/src/lib/CompanionWindowRegistry.js
@@ -0,0 +1,21 @@
+import ThumbnailNavigation from '../containers/ThumbnailNavigation';
+import WindowSideBarAnnotationsPanel from '../containers/WindowSideBarAnnotationsPanel';
+import WindowSideBarInfoPanel from '../containers/WindowSideBarInfoPanel';
+import WindowSideBarCanvasPanel from '../containers/WindowSideBarCanvasPanel';
+import AttributionPanel from '../containers/AttributionPanel';
+import SearchPanel from '../containers/SearchPanel';
+import LayersPanel from '../containers/LayersPanel';
+import CustomPanel from '../containers/CustomPanel';
+
+const map = {
+  annotations: WindowSideBarAnnotationsPanel,
+  attribution: AttributionPanel,
+  canvas: WindowSideBarCanvasPanel,
+  custom: CustomPanel,
+  info: WindowSideBarInfoPanel,
+  layers: LayersPanel,
+  search: SearchPanel,
+  thumbnailNavigation: ThumbnailNavigation,
+};
+
+export default map;