diff --git a/__tests__/integration/mirador/plugins/validate.html b/__tests__/integration/mirador/plugins/validate.html
new file mode 100644
index 0000000000000000000000000000000000000000..8906af8c971bd0cbece28fdab8e8ba6001ef9a35
--- /dev/null
+++ b/__tests__/integration/mirador/plugins/validate.html
@@ -0,0 +1,100 @@
+<!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' };
+
+      const validPluginA = {
+        target: 'WorkspaceControlPanelButtons',
+        mode: 'add',
+        component: props => (<div id="valid-plugin-a" />),
+      };
+
+      const validPluginB = {
+        name: 'validPluginB',
+        target: 'WorkspaceControlPanelButtons',
+        mode: 'add',
+        component: props => (<div id="valid-plugin-b" />),
+        mapStateToProps: () => ({}),
+        mapDispatchToProps: {},
+        reducers: {
+          bar: (state = null, action) => state,
+        },
+      };
+
+      const invalidPluginA = {
+        name: 'invalidPluginA',
+        target: 'WorkspaceControlPanelButtons',
+        mode: 'LURK', // invalid
+        component: props => (<div id="invalid-plugin-a" />),
+      };
+
+      const invalidPluginB = {
+        name: 'invalidPluginB',
+        target: x => x, // invalid
+        mode: 'add',
+        component: props => (<div id="invalid-plugin-b" />),
+      };
+
+      const invalidPluginC = {
+        name: 'invalidPluginC',
+        target: 'WorkspaceControlPanelButtons',
+        mode: 'add',
+        component: props => (<div id="invalid-plugin-c" />),
+        mapStateToProps: {}, // invalid
+      };
+
+      const invalidPluginD = {
+        name: 'invalidPluginD',
+        target: 'WorkspaceControlPanelButtons',
+        mode: 'add',
+        component: props => (<div id="invalid-plugin-d" />),
+        mapDispatchToProps: "foo" // invalid
+      };
+
+      const invalidPluginE = {
+        name: 'invalidPluginE',
+        target: 'WorkspaceControlPanelButtons',
+        mode: 'add',
+        component: props => (<div id="invalid-plugin-e" />),
+        reducers: 3, // invalid
+      };
+
+      const invalidPluginF = {
+        name: 'invalidPluginF',
+        target: 'WorkspaceControlPanelButtons',
+        mode: 'add',
+        component: props => (<div id="invalid-plugin-f" />),
+        reducers: {
+          foo: "foo", // invalid
+        },
+      };
+
+
+
+      const miradorInstance = Mirador.viewer(config, [
+        validPluginA,
+        validPluginB,
+        invalidPluginA,
+        invalidPluginB,
+        invalidPluginC,
+        invalidPluginD,
+        invalidPluginE,
+        invalidPluginF,
+      ]);
+
+     </script>
+  </body>
+</html>
diff --git a/__tests__/integration/mirador/plugins/validate.test.js b/__tests__/integration/mirador/plugins/validate.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..394257f726e40d18d41edcdc70a133a4b5476856
--- /dev/null
+++ b/__tests__/integration/mirador/plugins/validate.test.js
@@ -0,0 +1,22 @@
+
+describe('pass valid and invalid plugins to <WorkspaceControlPanelButtons>', () => {
+  beforeAll(async () => {
+    await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/plugins/validate.html');
+    await expect(page).toMatchElement('.mirador-viewer');
+    await page.waitFor(1000);
+  });
+
+  it('valid plugins will be applied <WorkspaceControlPanelButtons>', async () => {
+    await expect(page).toMatchElement('.mirador-workspace-control-panel-buttons #valid-plugin-a');
+    await expect(page).toMatchElement('.mirador-workspace-control-panel-buttons #valid-plugin-b');
+  });
+
+  it('invalid plugins will not be applied <WorkspaceControlPanelButtons>', async () => {
+    await expect(page).not.toMatchElement('.mirador-workspace-control-panel-buttons #invalid-plugin-a');
+    await expect(page).not.toMatchElement('.mirador-workspace-control-panel-buttons #invalid-plugin-b');
+    await expect(page).not.toMatchElement('.mirador-workspace-control-panel-buttons #invalid-plugin-c');
+    await expect(page).not.toMatchElement('.mirador-workspace-control-panel-buttons #invalid-plugin-d');
+    await expect(page).not.toMatchElement('.mirador-workspace-control-panel-buttons #invalid-plugin-e');
+    await expect(page).not.toMatchElement('.mirador-workspace-control-panel-buttons #invalid-plugin-f');
+  });
+});
diff --git a/__tests__/src/extend/pluginStore.test.js b/__tests__/src/extend/pluginStore.test.js
index c66e74d5004724c76cecdd01201893e94b7c1282..fa34f945c0f6665062bb261cafba5c809fda22ce 100644
--- a/__tests__/src/extend/pluginStore.test.js
+++ b/__tests__/src/extend/pluginStore.test.js
@@ -14,40 +14,63 @@ describe('getPlugins', () => {
     pluginStore.storePlugins();
     expect(pluginStore.getPlugins('target')).not.toBeDefined();
   });
+
   it('returns mode->plugins mapping for target', () => {
+    /** */
+    const component = x => x;
+
     const plugins = [
-      { mode: 'wrap', target: 'Window' },
-      { mode: 'wrap', target: 'Window' },
-      { mode: 'add', target: 'Window' },
-      { mode: 'add', target: 'Window' },
-
-      { mode: 'wrap', target: 'TopBar' },
-      { mode: 'wrap', target: 'TopBar' },
-      { mode: 'add', target: 'TopBar' },
-      { mode: 'add', target: 'TopBar' },
+      { component, mode: 'wrap', target: 'Window' },
+      { component, mode: 'wrap', target: 'Window' },
+      { component, mode: 'add', target: 'Window' },
+      { component, mode: 'add', target: 'Window' },
+
+      { component, mode: 'wrap', target: 'TopBar' },
+      { component, mode: 'wrap', target: 'TopBar' },
+      { component, mode: 'add', target: 'TopBar' },
+      { component, mode: 'add', target: 'TopBar' },
     ];
 
     pluginStore.storePlugins(plugins);
 
     expect(pluginStore.getPlugins('Window')).toEqual({
       add: [
-        { mode: 'add', target: 'Window' },
-        { mode: 'add', target: 'Window' },
+        { component, mode: 'add', target: 'Window' },
+        { component, mode: 'add', target: 'Window' },
       ],
       wrap: [
-        { mode: 'wrap', target: 'Window' },
-        { mode: 'wrap', target: 'Window' },
+        { component, mode: 'wrap', target: 'Window' },
+        { component, mode: 'wrap', target: 'Window' },
       ],
     });
 
     expect(pluginStore.getPlugins('TopBar')).toEqual({
       add: [
-        { mode: 'add', target: 'TopBar' },
-        { mode: 'add', target: 'TopBar' },
+        { component, mode: 'add', target: 'TopBar' },
+        { component, mode: 'add', target: 'TopBar' },
       ],
       wrap: [
-        { mode: 'wrap', target: 'TopBar' },
-        { mode: 'wrap', target: 'TopBar' },
+        { component, mode: 'wrap', target: 'TopBar' },
+        { component, mode: 'wrap', target: 'TopBar' },
+      ],
+    });
+  });
+
+  // see also pluginValidation.test.js
+  it('filter out invalid plugins', () => {
+    /** */
+    const component = x => x;
+
+    const plugins = [
+      { component, mode: 'add', target: 'Window' },
+      { component, mode: 'LURK', target: 'Window' },
+    ];
+
+    pluginStore.storePlugins(plugins);
+
+    expect(pluginStore.getPlugins('Window')).toEqual({
+      add: [
+        { component, mode: 'add', target: 'Window' },
       ],
     });
   });
diff --git a/__tests__/src/extend/pluginValidation.test.js b/__tests__/src/extend/pluginValidation.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..235e9ca16a6c9394965ecf21bfb991b57b29a7a0
--- /dev/null
+++ b/__tests__/src/extend/pluginValidation.test.js
@@ -0,0 +1,108 @@
+import { validatePlugin } from '../../../src/extend';
+
+/** */
+const createPlugin = props => ({
+  component: x => x,
+  mapDispatchToProps: x => x,
+  mapStateToProps: x => x,
+  mode: 'add',
+  name: 'test',
+  reducers: {
+    bar: x => x,
+    foo: x => x,
+  },
+  target: 'Window',
+  ...props,
+});
+
+describe('validatePlugin', () => {
+  it('return true if plugin is valid', () => {
+    const plugin = createPlugin();
+    expect(validatePlugin(plugin)).toBe(true);
+  });
+
+  it('plugin must be a object', () => {
+    const plugin = [];
+    expect(validatePlugin(plugin)).toBe(false);
+  });
+
+  it('name must be undefined or string', () => {
+    let plugin = createPlugin({ name: undefined });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ name: 'test' });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ name: [] });
+    expect(validatePlugin(plugin)).toBe(false);
+  });
+
+  it('target must be string', () => {
+    let plugin = createPlugin({ target: undefined });
+    expect(validatePlugin(plugin)).toBe(false);
+    plugin = createPlugin({ target: 'test' });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ target: [] });
+    expect(validatePlugin(plugin)).toBe(false);
+  });
+
+  it('mode must be "add" or "wrap"', () => {
+    let plugin = createPlugin({ mode: undefined });
+    expect(validatePlugin(plugin)).toBe(false);
+    plugin = createPlugin({ mode: 'somethink' });
+    expect(validatePlugin(plugin)).toBe(false);
+    plugin = createPlugin({ mode: 'add' });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ mode: 'wrap' });
+    expect(validatePlugin(plugin)).toBe(true);
+  });
+
+  it('component must be function', () => {
+    let plugin = createPlugin({ component: undefined });
+    expect(validatePlugin(plugin)).toBe(false);
+    plugin = createPlugin({ component: 'somethink' });
+    expect(validatePlugin(plugin)).toBe(false);
+    plugin = createPlugin({ component: x => x });
+    expect(validatePlugin(plugin)).toBe(true);
+  });
+
+  it('mapStateToProps must be undefined, null or function', () => {
+    let plugin = createPlugin({ mapStateToProps: undefined });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ mapStateToProps: x => x });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ mapStateToProps: null });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ mapStateToProps: 'something' });
+    expect(validatePlugin(plugin)).toBe(false);
+  });
+
+  it('mapDispatchToProps must be undefined, null, function or object', () => {
+    let plugin = createPlugin({ mapDispatchToProps: undefined });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ mapDispatchToProps: x => x });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ mapDispatchToProps: {} });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ mapDispatchToProps: null });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ mapDispatchToProps: 'something' });
+    expect(validatePlugin(plugin)).toBe(false);
+  });
+
+  it('reducers must be undefined or object', () => {
+    let plugin = createPlugin({ reducers: undefined });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ reducers: {} });
+    expect(validatePlugin(plugin)).toBe(true);
+    plugin = createPlugin({ reducers: 'something' });
+    expect(validatePlugin(plugin)).toBe(false);
+  });
+
+  it('each reducer must be a function', () => {
+    let reducers = { bar: x => x, foo: x => x };
+    let plugin = createPlugin({ reducers });
+    expect(validatePlugin(plugin)).toBe(true);
+    reducers = { bar: x => x, foo: undefined };
+    plugin = createPlugin({ reducers });
+    expect(validatePlugin(plugin)).toBe(false);
+  });
+});
diff --git a/src/extend/index.js b/src/extend/index.js
index 661d66ff66f8264b9fa5fe425ebe6cb277a67be1..d534dcaffb6e8c4ed65d661bc2cb94c63836b39b 100644
--- a/src/extend/index.js
+++ b/src/extend/index.js
@@ -1,2 +1,3 @@
 export * from './pluginStore';
 export * from './withPlugins';
+export * from './pluginValidation';
diff --git a/src/extend/pluginStore.js b/src/extend/pluginStore.js
index 437d07b15daed26f641e0eee734d246f523dff9c..0b191b3dd1e59a54f5bd57c6b81d912b5b95fb83 100644
--- a/src/extend/pluginStore.js
+++ b/src/extend/pluginStore.js
@@ -1,4 +1,5 @@
 import update from 'lodash/update';
+import { validatePlugin } from '.';
 
 export const pluginStore = {
   /**
@@ -20,8 +21,10 @@ export const pluginStore = {
    *
    * @param {Array} plugins
    */
-  storePlugins(plugins) {
-    this.pluginMap = mapPlugins(plugins || []);
+  storePlugins(plugins = []) {
+    const { validPlugins, invalidPlugins } = filterPlugins(plugins);
+    logInvalidPlugins(invalidPlugins);
+    this.pluginMap = mapPlugins(validPlugins);
   },
 };
 
@@ -48,3 +51,21 @@ function mapPlugins(plugins) {
     update(map, [plugin.target, plugin.mode], x => [...x || [], plugin])
   ), {});
 }
+
+/** */
+function filterPlugins(plugins) {
+  const filteredPlugins = { invalidPlugins: [], validPlugins: [] };
+  plugins.forEach(plugin => (
+    validatePlugin(plugin)
+      ? filteredPlugins.validPlugins.push(plugin)
+      : filteredPlugins.invalidPlugins.push(plugin)
+  ));
+  return filteredPlugins;
+}
+
+/** */
+function logInvalidPlugins(plugins) {
+  plugins.forEach(plugin => (
+    console.log(`Mirador: Plugin ${plugin.name} is not valid and was rejected.`)
+  ));
+}
diff --git a/src/extend/pluginValidation.js b/src/extend/pluginValidation.js
new file mode 100644
index 0000000000000000000000000000000000000000..2f70d050f7795653cf2cf47cae0372c8e8f79619
--- /dev/null
+++ b/src/extend/pluginValidation.js
@@ -0,0 +1,69 @@
+import isString from 'lodash/isString';
+import isUndefined from 'lodash/isUndefined';
+import isFunction from 'lodash/isFunction';
+import isObject from 'lodash/isObject';
+import isNull from 'lodash/isNull';
+import values from 'lodash/values';
+
+/** */
+export const validatePlugin = plugin => [
+  checkPlugin,
+  checkName,
+  checkTarget,
+  checkMode,
+  checkComponent,
+  checkMapStateToProps,
+  checkMapDispatchToProps,
+  checkReducers,
+].every(check => check(plugin));
+
+/** */
+const checkPlugin = plugin => isObject(plugin);
+
+/** */
+const checkName = (plugin) => {
+  const { name } = plugin;
+  return isUndefined(name) || isString(name);
+};
+
+/** */
+const checkTarget = (plugin) => {
+  const { target } = plugin;
+  return isString(target);
+};
+
+/** */
+const checkMode = (plugin) => {
+  const { mode } = plugin;
+  return ['add', 'wrap'].some(s => s === mode);
+};
+
+/** */
+const checkComponent = (plugin) => {
+  const { component } = plugin;
+  return isFunction(component);
+};
+
+/** */
+const checkMapStateToProps = (plugin) => {
+  const { mapStateToProps } = plugin;
+  return isUndefined(mapStateToProps)
+    || isNull(mapStateToProps)
+    || isFunction(mapStateToProps);
+};
+
+/** */
+const checkMapDispatchToProps = (plugin) => {
+  const { mapDispatchToProps } = plugin;
+  return isUndefined(mapDispatchToProps)
+    || isNull(mapDispatchToProps)
+    || isFunction(mapDispatchToProps)
+    || isObject(mapDispatchToProps);
+};
+
+/** */
+const checkReducers = (plugin) => {
+  const { reducers } = plugin;
+  return isUndefined(reducers)
+    || (isObject(reducers) && values(reducers).every(isFunction));
+};