diff --git a/packages/components/package.json b/packages/components/package.json
index 4b1366963b0acfd0e75f8cd3aefe328a57fff27e..c9c3193e27843611ab283c81c70eb7ca81313cae 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -18,6 +18,7 @@
     "config": "^1.28.1",
     "css-loader": "^0.28.7",
     "enzyme": "^3.3.0",
+    "enzyme-adapter-react-16": "^1.1.1",
     "express": "^4.15.4",
     "file-loader": "^0.11.2",
     "font-awesome": "^4.7.0",
@@ -30,8 +31,8 @@
     "pubsweet-client": "^2.5.1",
     "pubsweet-server": "^7.0.1",
     "pubsweet-theme-plugin": "^0.0.3",
-    "react": "^16.2.0",
-    "react-dom": "^16.2.0",
+    "react": "^16.3.2",
+    "react-dom": "^16.3.2",
     "react-router-dom": "^4.2.2",
     "react-test-renderer": "^16.2.0",
     "redux": "^3.7.2",
@@ -53,7 +54,7 @@
     ],
     "testPathIgnorePatterns": [
       "/node_modules",
-      "config/"
+      "config/test"
     ],
     "globals": {
       "PUBSWEET_COMPONENTS": [],
diff --git a/packages/components/xpub-edit/package.json b/packages/components/xpub-edit/package.json
index c0fe464735fa645a34552603aa0a4b70f7c3b8ff..0c0e746b99d3a88f720c80fc0ddb3ac28caa99fe 100644
--- a/packages/components/xpub-edit/package.json
+++ b/packages/components/xpub-edit/package.json
@@ -36,7 +36,7 @@
     "xpub-styleguide": "^0.0.12"
   },
   "peerDependencies": {
-    "react": ">=16"
+    "react": ">=16.3"
   },
   "scripts": {
     "styleguide": "styleguidist server",
diff --git a/packages/components/xpub-edit/src/components/Editor.js b/packages/components/xpub-edit/src/components/Editor.js
index 2178628422c3325e2b8f3c123058ee528307496a..d65826a67aaedc23ccb3ef84ddaf6c93fb52a6d4 100644
--- a/packages/components/xpub-edit/src/components/Editor.js
+++ b/packages/components/xpub-edit/src/components/Editor.js
@@ -53,7 +53,18 @@ class Editor extends React.Component {
   }
 
   render() {
-    const { options, title, readonly } = this.props
+    const {
+      autoFocus,
+      basePlaceholderClassName,
+      className,
+      onBlur,
+      options,
+      placeholder,
+      placeholderClassName,
+      title,
+      readonly,
+      ...remainingProps
+    } = this.props
     const { state } = this.state
     const menu = readonly ? {} : options.menu
 
@@ -68,7 +79,7 @@ class Editor extends React.Component {
           />
         )}
 
-        <div ref={this.createEditorView} />
+        <div ref={this.createEditorView} {...remainingProps} />
       </div>
     )
   }
diff --git a/packages/components/xpub-edit/src/components/HtmlEditor.js b/packages/components/xpub-edit/src/components/HtmlEditor.js
index 442dabd2f265d57d049211d993458ca9e391eb96..1913c77a488225eb90d889e3fd4f80e7c54c3204 100644
--- a/packages/components/xpub-edit/src/components/HtmlEditor.js
+++ b/packages/components/xpub-edit/src/components/HtmlEditor.js
@@ -48,27 +48,9 @@ class HtmlEditor extends React.Component {
   }
 
   render() {
-    const {
-      options,
-      className,
-      placeholder,
-      placeholderClassName,
-      title,
-      readonly,
-    } = this.props
+    const { value, onChange, onBlur, ...passedProps } = this.props
 
-    return (
-      <Editor
-        className={className}
-        onBlur={this.onBlur}
-        onChange={this.onChange}
-        options={options}
-        placeholder={placeholder}
-        placeholderClassName={placeholderClassName}
-        readonly={readonly}
-        title={title}
-      />
-    )
+    return <Editor onChange={this.onChange} {...passedProps} />
   }
 }
 
diff --git a/packages/components/xpub-edit/src/components/HtmlViewer.js b/packages/components/xpub-edit/src/components/HtmlViewer.js
index dbda30c6a759996c03607c9b84baca3fbbc6ce92..a898f435c0267732656375e4f798ea3fad57ad1b 100644
--- a/packages/components/xpub-edit/src/components/HtmlViewer.js
+++ b/packages/components/xpub-edit/src/components/HtmlViewer.js
@@ -24,9 +24,9 @@ class HtmlViewer extends React.Component {
   }
 
   render() {
-    const { options, className, value } = this.props
+    const { options, value, ...props } = this.props
     options.doc = this.changeContentValue(value)
-    return <Viewer className={className} options={options} />
+    return <Viewer options={options} {...props} />
   }
 }
 
diff --git a/packages/components/xpub-edit/src/components/configurable/ConfigurableEditor.js b/packages/components/xpub-edit/src/components/configurable/ConfigurableEditor.js
index 620a94edd923ef5bb19b399d14998a06a97e3817..2fd1b158ff5ef3ea065d8cb77ec141d871dc9975 100644
--- a/packages/components/xpub-edit/src/components/configurable/ConfigurableEditor.js
+++ b/packages/components/xpub-edit/src/components/configurable/ConfigurableEditor.js
@@ -1,29 +1,30 @@
 import React from 'react'
+import PropTypes from 'prop-types'
 import HtmlEditor from '../HtmlEditor'
-import makeConfig from './config'
+import EditorOptions from './EditorOptions'
 
-const ConfigurableEditor = ({
-  className,
-  value,
-  placeholder,
-  placeholderClassName,
-  title,
-  onBlur,
-  onChange,
-  readonly,
-  ...features
-}) => (
-  <HtmlEditor
-    className={className}
-    onBlur={onBlur}
-    onChange={onChange}
-    options={makeConfig(features)}
-    placeholder={placeholder}
-    placeholderClassName={placeholderClassName}
-    readonly={readonly}
-    title={title}
-    value={value}
-  />
+const ConfigurableEditor = props => (
+  <EditorOptions {...props}>
+    {(options, passedProps) => (
+      <HtmlEditor options={options} {...passedProps} />
+    )}
+  </EditorOptions>
 )
 
+ConfigurableEditor.propTypes = {
+  bold: PropTypes.bool,
+  italic: PropTypes.bool,
+  underline: PropTypes.bool,
+  superscript: PropTypes.bool,
+  subscript: PropTypes.bool,
+  smallcaps: PropTypes.bool,
+  link: PropTypes.bool,
+  heading: PropTypes.bool,
+  undo: PropTypes.bool,
+  redo: PropTypes.bool,
+  onChange: PropTypes.func.isRequired,
+  onBlur: PropTypes.func.isRequired,
+  value: PropTypes.string.isRequired,
+}
+
 export default ConfigurableEditor
diff --git a/packages/components/xpub-edit/src/components/configurable/ConfigurableEditor.md b/packages/components/xpub-edit/src/components/configurable/ConfigurableEditor.md
index 08704cfa053d08edd2ea337e161841f9248ece4a..4aafd16cecc14c761a103e231cc1e3ade6d99e37 100644
--- a/packages/components/xpub-edit/src/components/configurable/ConfigurableEditor.md
+++ b/packages/components/xpub-edit/src/components/configurable/ConfigurableEditor.md
@@ -1,4 +1,4 @@
-An editor that can be configured simply with boolean props
+An editor whose features can be configured with boolean props
 
 ```js
 const value = faker.lorem.sentence(20)
diff --git a/packages/components/xpub-edit/src/components/configurable/ConfigurableViewer.js b/packages/components/xpub-edit/src/components/configurable/ConfigurableViewer.js
index c9a46cfb9615d5ea245749c5dc7495c727344bc7..f1f52e17d6c16da0842c49bc03a4521b90710fbe 100644
--- a/packages/components/xpub-edit/src/components/configurable/ConfigurableViewer.js
+++ b/packages/components/xpub-edit/src/components/configurable/ConfigurableViewer.js
@@ -1,13 +1,27 @@
 import React from 'react'
+import PropTypes from 'prop-types'
 import HtmlViewer from '../HtmlViewer'
-import makeConfig from './config'
+import EditorOptions from './EditorOptions'
 
-const ConfigurableViewer = ({ className, value, ...features }) => (
-  <HtmlViewer
-    className={className}
-    options={makeConfig(features)}
-    value={value}
-  />
+const ConfigurableViewer = props => (
+  <EditorOptions {...props}>
+    {(options, remainingProps) => (
+      <HtmlViewer options={options} {...remainingProps} />
+    )}
+  </EditorOptions>
 )
 
+ConfigurableViewer.propTypes = {
+  bold: PropTypes.bool,
+  italic: PropTypes.bool,
+  underline: PropTypes.bool,
+  superscript: PropTypes.bool,
+  subscript: PropTypes.bool,
+  smallcaps: PropTypes.bool,
+  link: PropTypes.bool,
+  heading: PropTypes.bool,
+  undo: PropTypes.bool,
+  redo: PropTypes.bool,
+}
+
 export default ConfigurableViewer
diff --git a/packages/components/xpub-edit/src/components/configurable/ConfigurableViewer.md b/packages/components/xpub-edit/src/components/configurable/ConfigurableViewer.md
new file mode 100644
index 0000000000000000000000000000000000000000..7cd17ff36b916b0f2218edcbc14cdea65cd19db6
--- /dev/null
+++ b/packages/components/xpub-edit/src/components/configurable/ConfigurableViewer.md
@@ -0,0 +1,6 @@
+A viewer whose features can be configured with boolean props
+
+```js
+const value = faker.lorem.sentence(200)
+;<ConfigurableViewer value={value} bold italic link />
+```
diff --git a/packages/components/xpub-edit/src/components/configurable/EditorOptions.js b/packages/components/xpub-edit/src/components/configurable/EditorOptions.js
new file mode 100644
index 0000000000000000000000000000000000000000..0445e48a0b0ffc5fc18c5c3b930250d06d0bf361
--- /dev/null
+++ b/packages/components/xpub-edit/src/components/configurable/EditorOptions.js
@@ -0,0 +1,49 @@
+import React from 'react'
+import makeConfig from './config'
+
+const FEATURES_WHITELIST = [
+  'bold',
+  'italic',
+  'underline',
+  'superscript',
+  'subscript',
+  'smallcaps',
+  'link',
+  'heading',
+  'undo',
+  'redo',
+]
+
+class ConfigurableEditor extends React.Component {
+  constructor(props) {
+    super(props)
+    this.state = {}
+  }
+
+  static getDerivedStateFromProps(nextProps, prevState) {
+    const features = Object.keys(nextProps)
+      .filter(key => FEATURES_WHITELIST.includes(key))
+      .filter(key => nextProps[key])
+
+    if (prevState.options) {
+      // updating options on an existing editor is deliberately disabled
+      // as it causes menu buttons to forget their selected state
+      return null
+    }
+
+    return {
+      options: makeConfig(features),
+    }
+  }
+
+  render() {
+    // only pass through unrecognised props
+    const remainingProps = Object.keys(this.props)
+      .filter(key => !FEATURES_WHITELIST.includes(key) && key !== 'children')
+      .reduce((props, key) => ({ ...props, [key]: this.props[key] }), {})
+
+    return this.props.children(this.state.options, remainingProps)
+  }
+}
+
+export default ConfigurableEditor
diff --git a/packages/components/xpub-edit/src/components/configurable/EditorOptions.test.js b/packages/components/xpub-edit/src/components/configurable/EditorOptions.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..3a2007821c6def0e99957ffe503f66e84b7b63a8
--- /dev/null
+++ b/packages/components/xpub-edit/src/components/configurable/EditorOptions.test.js
@@ -0,0 +1,33 @@
+import React from 'react'
+import { shallow } from 'enzyme'
+import EditorOptions from './EditorOptions'
+
+function makeWrapper(props) {
+  return shallow(<EditorOptions {...props} />)
+}
+
+describe('EditorOptions', () => {
+  it('passes menu to child editor', () => {
+    const children = jest.fn()
+    makeWrapper({ bold: true, italic: true, underline: false, children })
+
+    expect(children).toHaveBeenCalled()
+    expect(children.mock.calls[0][0].menu).toMatchObject([
+      { title: 'Toggle bold' },
+      { title: 'Toggle italic' },
+    ])
+  })
+
+  it('ignores change of props', () => {
+    const children = jest.fn()
+    const wrapper = makeWrapper({ bold: true, children })
+    wrapper.setProps({ bold: false })
+    expect(children.mock.calls[0][0]).toBe(children.mock.calls[1][0])
+  })
+
+  it('passes down arbitrary props', () => {
+    const children = jest.fn()
+    makeWrapper({ bold: true, foo: 'bar', children })
+    expect(children.mock.calls[0][1]).toEqual({ foo: 'bar' })
+  })
+})
diff --git a/packages/components/xpub-edit/src/components/configurable/config/config.test.js b/packages/components/xpub-edit/src/components/configurable/config/config.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..1705bb71456530b669dbd899912d3758a6472566
--- /dev/null
+++ b/packages/components/xpub-edit/src/components/configurable/config/config.test.js
@@ -0,0 +1,34 @@
+import makeConfig from '.'
+
+describe('makeConfig', () => {
+  it('turns an array of features into a config', () => {
+    const config = makeConfig([])
+
+    expect(config.schema).toBeTruthy()
+    expect(config.menu).toBeTruthy()
+    expect(config.plugins).toBeTruthy()
+  })
+
+  it('adds marks', () => {
+    const features = [
+      'bold',
+      'italic',
+      'underline',
+      'link',
+      'smallcaps',
+      'not real',
+      'superscript',
+      'subscript',
+    ]
+    const config = makeConfig(features)
+    const realFeatures = features.filter(f => f !== 'not real')
+
+    expect(Object.keys(config.schema.marks)).toEqual(realFeatures)
+  })
+
+  it('adds nodes', () => {
+    const config = makeConfig(['bold', 'heading'])
+
+    expect(Object.keys(config.schema.nodes)).toContain('heading')
+  })
+})
diff --git a/packages/components/xpub-edit/src/components/configurable/config/index.js b/packages/components/xpub-edit/src/components/configurable/config/index.js
index 25297a72f46c5e6bb5b88d6b971ab8d9894d4122..4310350aa176eff2768671176cb4ee3515cb971d 100644
--- a/packages/components/xpub-edit/src/components/configurable/config/index.js
+++ b/packages/components/xpub-edit/src/components/configurable/config/index.js
@@ -8,17 +8,15 @@ import nodes from './nodes'
 import marks from './marks'
 
 export default features => {
-  const featureNames = Object.keys(features)
-
   const schema = new Schema({
-    marks: pick(marks, featureNames),
+    marks: pick(marks, features),
     nodes: {
       ...pick(nodes, ['doc', 'paragraph', 'text']),
-      ...pick(nodes, featureNames),
+      ...pick(nodes, features),
     },
   })
 
-  const enabledMenuItems = pick(menuItems, featureNames)
+  const enabledMenuItems = pick(menuItems, features)
   const menu = map(enabledMenuItems, itemCreator => itemCreator(schema))
   const plugins = makePlugins(schema, features)
 
diff --git a/packages/components/xpub-edit/src/components/configurable/config/keys.js b/packages/components/xpub-edit/src/components/configurable/config/keys.js
index 87f388e3b15ec1c05bf8203b3b33b02c0cba7a05..b00ac82a3ad4530686e3a751036430a6d076e7ea 100644
--- a/packages/components/xpub-edit/src/components/configurable/config/keys.js
+++ b/packages/components/xpub-edit/src/components/configurable/config/keys.js
@@ -23,10 +23,15 @@ const makeKeymap = (schema, features) => {
     'Shift-Ctrl-0': setBlockType(schema.nodes.paragraph),
   }
 
-  if (features.bold) keys['Mod-b'] = toggleMark(schema.marks.bold)
-  if (features.italic) keys['Mod-i'] = toggleMark(schema.marks.italic)
-  if (features.heading)
+  if (features.includes('bold')) {
+    keys['Mod-b'] = toggleMark(schema.marks.bold)
+  }
+  if (features.includes('italic')) {
+    keys['Mod-i'] = toggleMark(schema.marks.italic)
+  }
+  if (features.includes('heading')) {
     keys['Shift-Ctrl-1'] = setBlockType(schema.nodes.heading, { level: 1 })
+  }
 
   Object.keys(baseKeymap).forEach(key => {
     if (keys[key]) {
diff --git a/yarn.lock b/yarn.lock
index 85a7d506ce1642fcf07b95f8dd69d6251db3e7a0..4da8f5d864b0741094d7573f23f03adf7626abcb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -222,6 +222,15 @@
     mkdirp "^0.5.1"
     rimraf "^2.5.2"
 
+"@pubsweet/default-theme@^0.2.0":
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/@pubsweet/default-theme/-/default-theme-0.2.2.tgz#23a229e74a7bdc63b951efe0f0bbd830fd5faa98"
+  dependencies:
+    styled-components "^3.2.5"
+    typeface-noto-sans "^0.0.54"
+    typeface-noto-serif "^0.0.54"
+    typeface-ubuntu-mono "^0.0.54"
+
 "@pubsweet/starter@git+https://gitlab.coko.foundation/pubsweet/pubsweet-starter.git":
   version "1.0.0-alpha.1"
   resolved "git+https://gitlab.coko.foundation/pubsweet/pubsweet-starter.git#7ff5635e266415ecb4969658175af8ae9940d791"
@@ -251,6 +260,38 @@
     pubsweet-theme-plugin "^0.0.3"
     react-router-redux next
 
+"@pubsweet/theme@^0.1.3":
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/@pubsweet/theme/-/theme-0.1.3.tgz#2c508abc284a33ce14f1fa0a08486477def259a4"
+  dependencies:
+    cokourier-prime-sans "git+https://gitlab.coko.foundation/julientaq/cokourier-sans-prime.git"
+    typeface-fira-mono "^0.0.43"
+    typeface-fira-sans "^0.0.43"
+    typeface-fira-sans-condensed "^0.0.43"
+    typeface-vollkorn "^0.0.43"
+
+"@pubsweet/ui@^3.0.0":
+  version "3.3.4"
+  resolved "https://registry.yarnpkg.com/@pubsweet/ui/-/ui-3.3.4.tgz#f4dda1e900268a7c460f01aec40da187518b6363"
+  dependencies:
+    babel-jest "^21.2.0"
+    classnames "^2.2.5"
+    enzyme "^3.2.0"
+    enzyme-adapter-react-16 "^1.1.1"
+    invariant "^2.2.3"
+    lodash "^4.17.4"
+    prop-types "^15.5.10"
+    react "^16.2.0"
+    react-dom "^16.2.0"
+    react-feather "^1.0.8"
+    react-redux "^5.0.2"
+    react-router-dom "^4.2.2"
+    react-tag-autocomplete "^5.5.0"
+    recompose "^0.26.0"
+    redux "^3.6.0"
+    redux-form "^7.0.3"
+    styled-components "^3.2.5"
+
 "@types/async@2.0.47":
   version "2.0.47"
   resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.47.tgz#f49ba1dd1f189486beb6e1d070a850f6ab4bd521"
@@ -5538,7 +5579,7 @@ i@0.3.x:
   version "0.3.6"
   resolved "https://registry.yarnpkg.com/i/-/i-0.3.6.tgz#d96c92732076f072711b6b10fd7d4f65ad8ee23d"
 
-iconv-lite@0.4.19, iconv-lite@^0.4.17, iconv-lite@~0.4.13:
+iconv-lite@0.4.19, iconv-lite@^0.4.17:
   version "0.4.19"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
 
@@ -5548,6 +5589,12 @@ iconv-lite@^0.4.4:
   dependencies:
     safer-buffer "^2.1.0"
 
+iconv-lite@~0.4.13:
+  version "0.4.23"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
 icss-replace-symbols@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
@@ -9481,9 +9528,9 @@ react-docgen@^3.0.0-beta9:
     node-dir "^0.1.10"
     recast "^0.12.6"
 
-react-dom@^16.2.0:
-  version "16.2.0"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044"
+react-dom@^16.2.0, react-dom@^16.3.2:
+  version "16.3.2"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df"
   dependencies:
     fbjs "^0.8.16"
     loose-envify "^1.1.0"
@@ -9527,6 +9574,10 @@ react-input-autosize@^2.1.2:
   dependencies:
     prop-types "^15.5.8"
 
+react-is@^16.3.2:
+  version "16.3.2"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22"
+
 react-moment@^0.6.1:
   version "0.6.9"
   resolved "https://registry.yarnpkg.com/react-moment/-/react-moment-0.6.9.tgz#f13a0ccedaf65b5fb8b1467d9f91c5346d74c3e2"
@@ -9741,12 +9792,13 @@ react-tag-autocomplete@^5.5.0:
   resolved "https://registry.yarnpkg.com/react-tag-autocomplete/-/react-tag-autocomplete-5.5.0.tgz#49841388b88323f6bccb0c10039bd0252875b49f"
 
 react-test-renderer@^16.0.0-0, react-test-renderer@^16.2.0:
-  version "16.2.0"
-  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.2.0.tgz#bddf259a6b8fcd8555f012afc8eacc238872a211"
+  version "16.3.2"
+  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.3.2.tgz#3d1ed74fda8db42521fdf03328e933312214749a"
   dependencies:
     fbjs "^0.8.16"
     object-assign "^4.1.1"
     prop-types "^15.6.0"
+    react-is "^16.3.2"
 
 react-transition-group@^1.1.2:
   version "1.2.1"
@@ -9769,9 +9821,9 @@ react-transition-group@^2.0.0, react-transition-group@^2.2.0:
     prop-types "^15.5.8"
     warning "^3.0.0"
 
-react@^16.2.0:
-  version "16.2.0"
-  resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"
+react@^16.2.0, react@^16.3.2:
+  version "16.3.2"
+  resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9"
   dependencies:
     fbjs "^0.8.16"
     loose-envify "^1.1.0"
@@ -10499,7 +10551,7 @@ safe-regex@^1.1.0:
   dependencies:
     ret "~0.1.10"
 
-safer-buffer@^2.1.0:
+"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
 
@@ -11783,8 +11835,8 @@ typeface-vollkorn@^0.0.54:
   resolved "https://registry.yarnpkg.com/typeface-vollkorn/-/typeface-vollkorn-0.0.54.tgz#1288bcd7d81c3dd7cd419e4448580d2a0b0640b2"
 
 ua-parser-js@^0.7.9:
-  version "0.7.17"
-  resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
+  version "0.7.18"
+  resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed"
 
 uglify-es@^3.3.4:
   version "3.3.9"
@@ -12347,8 +12399,8 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
     iconv-lite "0.4.19"
 
 whatwg-fetch@>=0.10.0:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
 
 whatwg-url@^6.4.0:
   version "6.4.0"