diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000000000000000000000000000000000000..b3caa59e110484e128762039cca9c1c5c0645099
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,31 @@
+{
+  "env": {
+    "jest/globals": true
+  },
+  "extends": ["airbnb","react-app"],
+  "globals": {
+    "page": true,
+    "document": true
+  },
+  "parser": "babel-eslint",
+  "plugins": ["jest"],
+  "rules": {
+    "import/prefer-default-export": "off",
+    "no-console": "off",
+    "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
+    "require-jsdoc": ["error", {
+      "require": {
+        "FunctionDeclaration": true,
+        "MethodDefinition": true,
+        "ClassDeclaration": true,
+        "ArrowFunctionExpression": true,
+        "FunctionExpression": true
+      }
+    }],
+    "react/prefer-stateless-function": "off",
+    "sort-keys": ["error", "asc", {
+      "caseSensitive": false,
+      "natural": false
+    }]
+  }
+}
diff --git a/__tests__/.eslintrc b/__tests__/.eslintrc
new file mode 100644
index 0000000000000000000000000000000000000000..17e9d54b941f4d8e67332646f10d4dcadc803aaf
--- /dev/null
+++ b/__tests__/.eslintrc
@@ -0,0 +1,5 @@
+{
+  "rules": {
+    "react/jsx-props-no-spreading": [0],
+  }
+}
diff --git a/__tests__/miradorAnnotationPlugin.test.js b/__tests__/miradorAnnotationPlugin.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..4551606273ca98497d450a6dbafaa51556deb63e
--- /dev/null
+++ b/__tests__/miradorAnnotationPlugin.test.js
@@ -0,0 +1,22 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import miradorAnnotationPlugin from '../src/plugins/miradorAnnotationPlugin';
+
+/** */
+function createWrapper(props) {
+  return shallow(
+    <miradorAnnotationPlugin.component
+      TargetComponent={'<div>hello</div>'}
+      targetProps={{}}
+      {...props}
+    />,
+  );
+}
+
+describe('MiradorAnnotation', () => {
+  let wrapper;
+  it('renders MiradorAnnotation', () => {
+    wrapper = createWrapper();
+    expect(wrapper.text()).toBe('HelloWorld');
+  });
+});
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..67796265100b1c7423d4d4d4b3e0efd56000eef6
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,13 @@
+module.exports = {
+  presets: [
+    [
+      '@babel/preset-env',
+      {
+        targets: {
+          node: 'current',
+        },
+      },
+    ],
+    '@babel/preset-react',
+  ],
+};
diff --git a/jest.config.js b/jest.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..22585e0f83302cdbc192b4e66f5d9da90abd844c
--- /dev/null
+++ b/jest.config.js
@@ -0,0 +1,17 @@
+// For a detailed explanation regarding each configuration property, visit:
+// https://jestjs.io/docs/en/configuration.html
+
+module.exports = {
+  // Automatically clear mock calls and instances between every test
+  clearMocks: true,
+
+  // The directory where Jest should output its coverage files
+  coverageDirectory: 'coverage',
+  setupFiles: [
+    '<rootDir>/setupJest.js',
+  ],
+  // Ignore Mirador code from jest transforms
+  transformIgnorePatterns: [
+    '<rootDir>/node_modules/(?!mirador)',
+  ],
+};
diff --git a/nwb.config.js b/nwb.config.js
index 146d011b3974eb2035edf870ebbd9400e27c4ad9..509e5a75bdeb087340976ed93ffcbb21553e38e1 100644
--- a/nwb.config.js
+++ b/nwb.config.js
@@ -1,7 +1,22 @@
+const path = require('path');
+
 module.exports = {
   type: 'react-component',
   npm: {
     esModules: true,
-    umd: false
-  }
-}
+    umd: {
+      global: 'MiradorAnnotation',
+      externals: {
+        react: 'React',
+      },
+    },
+  },
+  webpack: {
+    aliases: {
+      '@material-ui/core': path.resolve('./', 'node_modules', '@material-ui/core'),
+      '@material-ui/styles': path.resolve('./', 'node_modules', '@material-ui/styles'),
+      react: path.resolve('./', 'node_modules', 'react'),
+      'react-dom': path.resolve('./', 'node_modules', 'react-dom'),
+    },
+  },
+};
diff --git a/package.json b/package.json
index 4de84296fd731b278587baf9f74286673e9961cc..0a76794c2745276a66e61f0f4a701eb97c0207c0 100644
--- a/package.json
+++ b/package.json
@@ -13,11 +13,12 @@
   "scripts": {
     "build": "nwb build-react-component",
     "clean": "nwb clean-module && nwb clean-demo",
+    "lint": "eslint ./src ./__tests__",
     "prepublishOnly": "npm run build",
     "start": "nwb serve-react-demo",
-    "test": "nwb test-react",
-    "test:coverage": "nwb test-react --coverage",
-    "test:watch": "nwb test-react --server"
+    "test": "npm run lint && jest",
+    "test:coverage": "jest --coverage",
+    "test:watch": "jest --watch"
   },
   "dependencies": {},
   "peerDependencies": {
@@ -25,6 +26,22 @@
     "react": "16.x"
   },
   "devDependencies": {
+    "@babel/core": "^7.9.6",
+    "@babel/preset-env": "^7.9.6",
+    "@babel/preset-react": "^7.9.4",
+    "babel-eslint": "^10.1.0",
+    "enzyme": "^3.11.0",
+    "enzyme-adapter-react-16": "^1.15.2",
+    "eslint": "^6.8.0",
+    "eslint-config-airbnb": "^18.1.0",
+    "eslint-config-react-app": "^5.2.1",
+    "eslint-plugin-flowtype": "^4.7.0",
+    "eslint-plugin-import": "^2.20.2",
+    "eslint-plugin-jest": "^23.9.0",
+    "eslint-plugin-jsx-a11y": "^6.2.3",
+    "eslint-plugin-react": "^7.19.0",
+    "eslint-plugin-react-hooks": "^4.0.0",
+    "jest": "^26.0.1",
     "mirador": "^3.0.0-beta.8",
     "nwb": "0.24.x",
     "react": "^16.13.1",
diff --git a/setupJest.js b/setupJest.js
new file mode 100644
index 0000000000000000000000000000000000000000..7d1129afe68e71199c666cbfa37874c8e7d81121
--- /dev/null
+++ b/setupJest.js
@@ -0,0 +1,4 @@
+import Enzyme from 'enzyme'; // eslint-disable-line import/no-extraneous-dependencies
+import Adapter from 'enzyme-adapter-react-16'; // eslint-disable-line import/no-extraneous-dependencies
+
+Enzyme.configure({ adapter: new Adapter() });
diff --git a/src/index.js b/src/index.js
index 782b893896a76894e426caba75214fbfbf631f44..b2aab9ada80f2917f2580a72b2d867f6f1f02255 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1 +1 @@
- export miradorAnnotationPlugin from './plugins/miradorAnnotationPlugin';
+export miradorAnnotationPlugin from './plugins/miradorAnnotationPlugin';
diff --git a/src/plugins/miradorAnnotationPlugin.js b/src/plugins/miradorAnnotationPlugin.js
index f5dec8771252610cd6ae77188295555ee196e312..a513e1600b222751d2141b7e12c1f803514bee15 100644
--- a/src/plugins/miradorAnnotationPlugin.js
+++ b/src/plugins/miradorAnnotationPlugin.js
@@ -1,15 +1,17 @@
-import React, { Component } from 'react'
+import React, { Component } from 'react';
 
-class MiradorImageTools extends Component {
+/** */
+class MiradorAnnotation extends Component {
+  /** */
   render() {
     return (
       <div>HelloWorld</div>
-    )
+    );
   }
 }
 
 export default {
-  target: 'WindowTopMenu',
+  component: MiradorAnnotation,
   mode: 'add',
-  component: MiradorImageTools,
-}
+  target: 'WindowTopMenu',
+};
diff --git a/tests/.eslintrc b/tests/.eslintrc
deleted file mode 100644
index 7eeefc33b66c055f211388c46d17b50c45db7c25..0000000000000000000000000000000000000000
--- a/tests/.eslintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "env": {
-    "mocha": true
-  }
-}
diff --git a/tests/index.test.js b/tests/index.test.js
deleted file mode 100644
index 0d009097e6671ea2c7f9f2792ed03beb461df28c..0000000000000000000000000000000000000000
--- a/tests/index.test.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import expect from 'expect'
-import React from 'react'
-import {render, unmountComponentAtNode} from 'react-dom'
-
-import Component from 'src/'
-
-describe('Component', () => {
-  let node
-
-  beforeEach(() => {
-    node = document.createElement('div')
-  })
-
-  afterEach(() => {
-    unmountComponentAtNode(node)
-  })
-
-  it('displays a welcome message', () => {
-    render(<Component/>, node, () => {
-      expect(node.innerHTML).toContain('Welcome to React components')
-    })
-  })
-})