diff --git a/__tests__/src/extend/withPlugins.js b/__tests__/src/extend/withPlugins.js
new file mode 100644
index 0000000000000000000000000000000000000000..31f4fede990ec7f72abbefee0de630095fd69f3c
--- /dev/null
+++ b/__tests__/src/extend/withPlugins.js
@@ -0,0 +1,107 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import { withPlugins } from '../../../src/extend';
+import { pluginStore } from '../../../src/extend/pluginStore';
+
+
+jest.mock('../../../src/extend/pluginStore');
+
+/** */
+const Target = props => <div>Hello</div>;
+
+/** create wrapper  */
+function createWrapper(plugins) {
+  pluginStore.getPlugins = () => plugins;
+  const props = { foo: 1, bar: 2 };
+  const PluginWrapper = withPlugins('Target', Target);
+  return shallow(<PluginWrapper {...props} />);
+}
+
+describe('withPlugins', () => {
+  it('should return a function (normal function call)', () => {
+    expect(withPlugins('Target', Target)).toBeInstanceOf(Function);
+  });
+
+  it('should return a function (curry function call)', () => {
+    expect(withPlugins('Target')(Target)).toBeInstanceOf(Function);
+  });
+
+  it('displayName prop of returned function is based on target name argument', () => {
+    expect(withPlugins('Bubu', Target).displayName)
+      .toBe('WithPlugins(Bubu)');
+  });
+});
+
+describe('PluginHoc: if no plugin exists for the target', () => {
+  it('renders the target component', () => {
+    const wrapper = createWrapper([]);
+    expect(wrapper.find(Target).length).toBe(1);
+    expect(wrapper.find(Target).props().foo).toBe(1);
+    expect(wrapper.find(Target).props().bar).toBe(2);
+  });
+});
+
+describe('PluginHoc: if a delete plugin exists for the target', () => {
+  it('renders nothing', () => {
+    const plugin = {
+      target: 'Target',
+      mode: 'delete',
+    };
+    const wrapper = createWrapper([plugin]);
+    expect(wrapper.find('*').length).toBe(0);
+  });
+});
+
+describe('PluginHoc: if a replace plugin exists for the target', () => {
+  it('renders the plugin component', () => {
+    /** */
+    const PluginComponent = props => <div>look i am a plugin</div>;
+    const plugin = {
+      target: 'Target',
+      mode: 'replace',
+      component: PluginComponent,
+    };
+    const wrapper = createWrapper([plugin]);
+    const selector = 'Connect(PluginComponent)';
+    expect(wrapper.find(selector).length).toBe(1);
+    expect(wrapper.find(selector).props().foo).toBe(1);
+    expect(wrapper.find(selector).props().bar).toBe(2);
+  });
+});
+
+describe('PluginHoc: if a add plugin exists for the target', () => {
+  it('renders the target component and passes the plugin component as a prop', () => {
+    /** */
+    const PluginComponent = props => <div>look i am a plugin</div>;
+    const plugin = {
+      target: 'Target',
+      mode: 'add',
+      component: PluginComponent,
+    };
+    const wrapper = createWrapper([plugin]);
+    expect(wrapper.find(Target).length).toBe(1);
+    expect(wrapper.find(Target).props().foo).toBe(1);
+    expect(wrapper.find(Target).props().bar).toBe(2);
+    expect(wrapper.find(Target).props().PluginComponent.WrappedComponent)
+      .toBe(PluginComponent);
+  });
+});
+
+describe('PluginHoc: if a wrap plugin extists for the target', () => {
+  it('renders the plugin component and passes the target component as a prop', () => {
+    /** */
+    const PluginComponent = props => <div>look i am a plugin</div>;
+    const plugin = {
+      target: 'Target',
+      mode: 'wrap',
+      component: PluginComponent,
+    };
+    const wrapper = createWrapper([plugin]);
+    const selector = 'Connect(PluginComponent)';
+    expect(wrapper.find(selector).length).toBe(1);
+    expect(wrapper.find(selector).props().foo).toBe(1);
+    expect(wrapper.find(selector).props().bar).toBe(2);
+    expect(wrapper.find(selector).props().TargetComponent)
+      .toBe(Target);
+  });
+});
diff --git a/__tests__/src/lib/MiradorViewer.test.js b/__tests__/src/lib/MiradorViewer.test.js
index 90b81d3306c1f979cd87051d6d198d157248577d..5eb139764d36d3e5ca43cb6f3ba0e7063806461b 100644
--- a/__tests__/src/lib/MiradorViewer.test.js
+++ b/__tests__/src/lib/MiradorViewer.test.js
@@ -1,7 +1,10 @@
 import ReactDOM from 'react-dom';
+import { pluginStore } from '../../../src/extend';
 import MiradorViewer from '../../../src/lib/MiradorViewer';
 
 jest.unmock('react-i18next');
+jest.mock('../../../src/extend');
+jest.mock('react-dom');
 
 describe('MiradorViewer', () => {
   let instance;
@@ -20,26 +23,29 @@ describe('MiradorViewer', () => {
       expect(ReactDOM.render).toHaveBeenCalled();
     });
   });
-  describe('processPlugins', () => {
-    it('combines actionCreators and reducers', () => {
-      const fooPlugin = {
-        actions: {
-          fooAction: () => {},
-        },
-        reducers: {
-          fooReducer: null,
+  describe('process plugins', () => {
+    it('should store plugins and set reducers to state', () => {
+      /** */ const fooReducer = (state = 0) => state;
+      /** */ const barReducer = (state = 0) => state;
+      /** */ const bazReducer = (state = 0) => state;
+      /** */ const plugins = [
+        {
+          reducers: {
+            foo: fooReducer,
+            bar: barReducer,
+          },
         },
-      };
-      window.Mirador = {
-        plugins: {
-          fooPlugin,
+        {
+          reducers: {
+            baz: bazReducer,
+          },
         },
-      };
-      instance = new MiradorViewer({
-        plugins: ['fooPlugin'],
-      });
-      expect(instance.actions.fooAction).toBeDefined();
-      expect(instance.store.pluginReducers).toBeDefined();
+      ];
+      instance = new MiradorViewer({}, plugins);
+      expect(pluginStore.storePlugins).toBeCalledWith(plugins);
+      expect(instance.store.getState().foo).toBeDefined();
+      expect(instance.store.getState().bar).toBeDefined();
+      expect(instance.store.getState().baz).toBeDefined();
     });
   });
   describe('processConfig', () => {
diff --git a/package.json b/package.json
index f0a6eec10933b8b789d5efccf052bb3537069d40..9d8d741a6dbb7738525566873554ad5b57e5884c 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,7 @@
   "repository": "https://github.com/ProjectMirador/mirador",
   "size-limit": [
     {
-      "limit": "350 KB",
+      "limit": "360 KB",
       "path": "dist/mirador.min.js"
     }
   ],
diff --git a/src/extend/index.js b/src/extend/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..661d66ff66f8264b9fa5fe425ebe6cb277a67be1
--- /dev/null
+++ b/src/extend/index.js
@@ -0,0 +1,2 @@
+export * from './pluginStore';
+export * from './withPlugins';
diff --git a/src/extend/pluginStore.js b/src/extend/pluginStore.js
new file mode 100644
index 0000000000000000000000000000000000000000..19084ac8ed20cbb9efe0eb4147706a6a9b27b210
--- /dev/null
+++ b/src/extend/pluginStore.js
@@ -0,0 +1,10 @@
+export const pluginStore = {
+  /** */
+  storePlugins(plugins) {
+    this.plugins = plugins || [];
+  },
+  /** */
+  getPlugins() {
+    return this.plugins || [];
+  },
+};
diff --git a/src/extend/withPlugins.js b/src/extend/withPlugins.js
new file mode 100644
index 0000000000000000000000000000000000000000..681eca0a8d65ce719e5b1529ea81ede3578d953e
--- /dev/null
+++ b/src/extend/withPlugins.js
@@ -0,0 +1,43 @@
+import React, { Component } from 'react';
+import curry from 'lodash/curry';
+import { connect } from 'react-redux';
+import { pluginStore } from '.';
+
+/** withPlugins should be the innermost HOC */
+function _withPlugins(targetName, TargetComponent) { // eslint-disable-line no-underscore-dangle
+  /** plugin wrapper hoc */
+  class PluginHoc extends Component {
+    /** render */
+    render() {
+      const plugin = pluginStore.getPlugins().find(p => p.target === targetName);
+
+      if (plugin && plugin.mode === 'delete') {
+        return null;
+      }
+      if (plugin && plugin.mode === 'replace') {
+        const PluginComponent = connectPluginComponent(plugin);
+        return <PluginComponent {...this.props} />;
+      }
+      if (plugin && plugin.mode === 'add') {
+        const PluginComponent = connectPluginComponent(plugin);
+        return <TargetComponent {...this.props} PluginComponent={PluginComponent} />;
+      }
+      if (plugin && plugin.mode === 'wrap') {
+        const PluginComponent = connectPluginComponent(plugin);
+        return <PluginComponent {...this.props} TargetComponent={TargetComponent} />;
+      }
+      return <TargetComponent {...this.props} />;
+    }
+  }
+
+  PluginHoc.displayName = `WithPlugins(${targetName})`;
+  return PluginHoc;
+}
+
+/** Connect plugin component to state */
+function connectPluginComponent(plugin) {
+  return connect(plugin.mapStateToProps, plugin.mapDispatchToProps)(plugin.component);
+}
+
+/** withPlugins('MyComponent')(MyComponent) */
+export const withPlugins = curry(_withPlugins);
diff --git a/src/index.js b/src/index.js
index 89fcbb163453368df0375030687dd22d8c9846e3..660bc6849bf716a21fe2c8ee274ee37ad25cb6d3 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,12 +1,14 @@
 import init from './init';
+import * as actions from './state/actions';
+import * as selectors from './state/selectors';
 
 export * from './components';
-export * from './state/actions';
 export * from './state/reducers';
 
 const exports = {
   viewer: init,
-  plugins: {},
+  actions,
+  selectors,
 };
 
 export default exports;
diff --git a/src/init.js b/src/init.js
index 30ccb0096723584417f8bf53fec673e1e2cb9869..0f3f58b73db07565bd79be0d6dd4e83e8490fb91 100644
--- a/src/init.js
+++ b/src/init.js
@@ -3,6 +3,6 @@ import MiradorViewer from './lib/MiradorViewer';
 /**
  * Default Mirador instantiation
  */
-export default function (config) {
-  return new MiradorViewer(config);
+export default function (config, plugins) {
+  return new MiradorViewer(config, plugins);
 }
diff --git a/src/lib/MiradorViewer.js b/src/lib/MiradorViewer.js
index 30e9b27f9dea2bf1a04bb9972f189068f372f877..e9cf6296f624e6dc55e5adbdb2c640a36ed9774b 100644
--- a/src/lib/MiradorViewer.js
+++ b/src/lib/MiradorViewer.js
@@ -3,7 +3,7 @@ import ReactDOM from 'react-dom';
 import { Provider } from 'react-redux';
 import deepmerge from 'deepmerge';
 import App from '../containers/App';
-import createRootReducer from '../state/reducers/rootReducer';
+import { pluginStore } from '../extend';
 import createStore from '../state/createStore';
 import * as actions from '../state/actions';
 import settings from '../config/settings';
@@ -14,10 +14,11 @@ import settings from '../config/settings';
 class MiradorViewer {
   /**
    */
-  constructor(config) {
-    this.store = createStore();
+  constructor(config, plugins) {
+    pluginStore.storePlugins(plugins);
+    const pluginReducers = getReducersFromPlugins(plugins);
+    this.store = createStore(pluginReducers);
     this.config = config;
-    this.processPlugins();
     this.processConfig();
     const viewer = {
       actions,
@@ -70,40 +71,11 @@ class MiradorViewer {
       );
     });
   }
+}
 
-  /**
-   * Process Plugins
-   */
-  processPlugins() {
-    const plugins = this.config.plugins || [];
-    const actionCreators = [];
-    const reducers = [];
-
-    plugins.forEach((pluginName) => {
-      const plugin = window.Mirador.plugins[pluginName];
-
-      // Add Actions
-      if (plugin.actions) {
-        Object.keys(plugin.actions)
-          .forEach(actionName => actionCreators.push({
-            name: actionName,
-            action: plugin.actions[actionName],
-          }));
-      }
-      // Add Reducers
-      if (plugin.reducers) {
-        Object.keys(plugin.reducers)
-          .forEach(reducerName => reducers.push({
-            name: reducerName,
-            reducer: plugin.reducers[reducerName],
-          }));
-      }
-    });
-
-    actionCreators.forEach((action) => { actions[action.name] = action.action; });
-    reducers.forEach((reducer) => { this.store.pluginReducers[reducer.name] = reducer.reducer; });
-    this.store.replaceReducer(createRootReducer(this.store.pluginReducers));
-  }
+/** Return reducers from plugins */
+function getReducersFromPlugins(plugins) {
+  return plugins && plugins.reduce((acc, plugin) => ({ ...acc, ...plugin.reducers }), {});
 }
 
 export default MiradorViewer;
diff --git a/src/state/createStore.js b/src/state/createStore.js
index bf052d19b04c55dd8bc4e3219647582085f84bdb..9a30f6ab33710de5517aad1601aff11b489eafa2 100644
--- a/src/state/createStore.js
+++ b/src/state/createStore.js
@@ -13,9 +13,9 @@ import createRootReducer from './reducers/rootReducer';
 /**
  * Configure Store
  */
-export default function () {
-  const store = createStore(
-    createRootReducer(),
+export default function (pluginReducers) {
+  return createStore(
+    createRootReducer(pluginReducers),
     composeWithDevTools(
       applyMiddleware(
         createDebounce(),
@@ -23,6 +23,4 @@ export default function () {
       ),
     ),
   );
-  store.pluginReducers = {};
-  return store;
 }