diff --git a/packages/component-faraday-ui/src/AuthorCard.js b/packages/component-faraday-ui/src/AuthorCard.js
index 2ace7fa985268bb535cdeff598a1a5ac7a030433..36f74f2d439a408fe6024014e11d409564fec1d4 100644
--- a/packages/component-faraday-ui/src/AuthorCard.js
+++ b/packages/component-faraday-ui/src/AuthorCard.js
@@ -4,14 +4,7 @@ import styled from 'styled-components'
 import { th } from '@pubsweet/ui-toolkit'
 import { required } from 'xpub-validators'
 import { reduxForm, Field } from 'redux-form'
-import {
-  Menu,
-  H3,
-  ValidatedField,
-  TextField,
-  Checkbox,
-  Spinner,
-} from '@pubsweet/ui'
+import { H3, ValidatedField, TextField, Checkbox, Spinner } from '@pubsweet/ui'
 import {
   compose,
   withState,
@@ -20,7 +13,7 @@ import {
   setDisplayName,
 } from 'recompose'
 
-import { withCountries } from 'pubsweet-component-faraday-ui'
+import { MenuCountry } from 'pubsweet-component-faraday-ui'
 import { Tag, Label, Row, Item, PersonInfo, IconButton, OpenModal } from './'
 import { validators } from './helpers'
 
@@ -129,7 +122,6 @@ const AuthorTitle = ({
 
 // #region AuthorEdit
 const AuthorEdit = ({
-  countries,
   author,
   editMode,
   listIndex,
@@ -196,7 +188,7 @@ const AuthorEdit = ({
         <Label required>Country</Label>
         <ValidatedField
           component={input => (
-            <Menu {...input} options={countries} placeholder="Please select" />
+            <MenuCountry {...input} placeholder="Please select" />
           )}
           data-test-id="author-card-country"
           name="country"
@@ -208,7 +200,6 @@ const AuthorEdit = ({
 // #endregion
 
 const EnhancedAuthorEdit = compose(
-  withCountries,
   withProps(({ author }) => ({
     initialValues: author,
   })),
diff --git a/packages/component-faraday-ui/src/InviteReviewers.js b/packages/component-faraday-ui/src/InviteReviewers.js
index 7c93ebb316f9a1d7a10c664548a6ee94c6864cdd..7f07b70cba1b4e7944aea14142d5bcd1632a1fad 100644
--- a/packages/component-faraday-ui/src/InviteReviewers.js
+++ b/packages/component-faraday-ui/src/InviteReviewers.js
@@ -5,7 +5,9 @@ import { reduxForm } from 'redux-form'
 import { th } from '@pubsweet/ui-toolkit'
 import { required } from 'xpub-validators'
 import { withModal } from 'pubsweet-component-modal/src/components'
-import { Button, H4, Menu, TextField, ValidatedField } from '@pubsweet/ui'
+import { Button, H4, TextField, ValidatedField } from '@pubsweet/ui'
+
+import { MenuCountry } from 'pubsweet-component-faraday-ui'
 
 import {
   Row,
@@ -15,10 +17,9 @@ import {
   ItemOverrideAlert,
   withFetching,
   validators,
-  withCountries,
 } from '../'
 
-const InviteReviewers = ({ countries, handleSubmit, reset }) => (
+const InviteReviewers = ({ handleSubmit, reset }) => (
   <Root>
     <Row justify="space-between" mb={2}>
       <H4>Invite reviewer</H4>
@@ -82,7 +83,9 @@ const InviteReviewers = ({ countries, handleSubmit, reset }) => (
       <ItemOverrideAlert vertical>
         <Label required>Country</Label>
         <ValidatedField
-          component={input => <Menu options={countries} {...input} />}
+          component={input => (
+            <MenuCountry {...input} placeholder="Please select" />
+          )}
           name="country"
           validate={[required]}
         />
@@ -93,7 +96,6 @@ const InviteReviewers = ({ countries, handleSubmit, reset }) => (
 
 export default compose(
   withFetching,
-  withCountries,
   withModal(({ isFetching, modalKey }) => ({
     modalKey,
     isFetching,
diff --git a/packages/component-faraday-ui/src/MenuCountry.js b/packages/component-faraday-ui/src/MenuCountry.js
new file mode 100644
index 0000000000000000000000000000000000000000..8ca9f618bb2612ebdf04dea4a5c0e781ba2a39ad
--- /dev/null
+++ b/packages/component-faraday-ui/src/MenuCountry.js
@@ -0,0 +1,81 @@
+import React from 'react'
+import { Menu } from '@pubsweet/ui'
+import { startsWith, toLower, get } from 'lodash'
+import { compose, withState, withHandlers } from 'recompose'
+import styled from 'styled-components'
+import { th } from '@pubsweet/ui-toolkit'
+
+import { withCountries } from 'pubsweet-component-faraday-ui'
+
+const filteredCountries = (countries, userInput) =>
+  countries.filter(o => startsWith(toLower(o.label), toLower(userInput)))
+
+const firstFilteredCountry = props =>
+  filteredCountries(props.countries, props.userInput)[0]
+
+const CustomOpener = ({
+  selected,
+  userInput,
+  toggleMenu,
+  placeholder,
+  optionLabel,
+  onChange,
+  onEnter,
+}) => (
+  <Input
+    onChange={onChange}
+    onClick={toggleMenu}
+    onKeyUp={onEnter}
+    placeholder={selected ? optionLabel(selected) : placeholder}
+    value={userInput}
+  />
+)
+
+const MenuCountry = ({ countries = [], ...input }) => (
+  <Menu
+    {...input}
+    options={filteredCountries(countries, input.userInput)}
+    placeholder="Please select"
+    renderOpener={CustomOpener}
+  />
+)
+
+const enhance = compose(
+  withCountries,
+  withState('userInput', 'updateUserInput', ''),
+  withHandlers({
+    onChange: ({ updateUserInput, onChange }) => value => {
+      // this value is an input DOM event while typing and a dropdown value when
+      // selected
+      if (typeof value === 'string') {
+        onChange(value)
+      }
+      updateUserInput(get(value, 'target.value', ''))
+    },
+    onEnter: props => event => {
+      if (event.which === 13) {
+        props.onChange(firstFilteredCountry(props).value)
+        props.updateUserInput(firstFilteredCountry(props).label)
+      }
+    },
+  }),
+)
+
+export default enhance(MenuCountry)
+
+const Input = styled.input`
+  width: 100%;
+  height: calc(${th('gridUnit')} * 4);
+  border: ${th('accordion.border')};
+  border-radius: ${th('borderRadius')};
+  padding: 0 ${th('gridUnit')};
+  ::placeholder {
+    color: ${th('colorText')};
+    opacity: 1;
+    font-family: ${th('fontWriting')};
+  }
+  :focus {
+    border-color: ${th('action.colorActive')}
+    outline: none;
+  }
+`
diff --git a/packages/component-faraday-ui/src/UserProfile.js b/packages/component-faraday-ui/src/UserProfile.js
index 292c04822b7ee0bdbcfb2662a12a91bb961cc8ca..e1bdc2074f60d3a1810ab5e1f595b1289b564c00 100644
--- a/packages/component-faraday-ui/src/UserProfile.js
+++ b/packages/component-faraday-ui/src/UserProfile.js
@@ -7,7 +7,7 @@ import { th } from '@pubsweet/ui-toolkit'
 import { required as requiredValidator } from 'xpub-validators'
 import { compose, withStateHandlers, withProps } from 'recompose'
 import { H3, Spinner, ValidatedField, TextField, Menu } from '@pubsweet/ui'
-import { withCountries } from 'pubsweet-component-faraday-ui'
+import { withCountries, MenuCountry } from 'pubsweet-component-faraday-ui'
 
 import {
   Row,
@@ -179,7 +179,9 @@ const EditUserProfile = compose(
           <Item ml={1} vertical>
             <Label required>Country</Label>
             <ValidatedField
-              component={input => <Menu {...input} options={countries} />}
+              component={input => (
+                <MenuCountry {...input} placeholder="Please select" />
+              )}
               name="country"
               validate={[requiredValidator]}
             />
diff --git a/packages/component-faraday-ui/src/index.js b/packages/component-faraday-ui/src/index.js
index b5e2c10898e6ec568f8225b96930fc82da49d767..22f0696fd368ce3c11224b59907824ab23639fad 100644
--- a/packages/component-faraday-ui/src/index.js
+++ b/packages/component-faraday-ui/src/index.js
@@ -49,6 +49,7 @@ export { default as TextTooltip } from './TextTooltip'
 export { default as EditorialReportCard } from './EditorialReportCard'
 export { default as ReviewerReportAuthor } from './ReviewerReportAuthor'
 export { default as PasswordValidation } from './PasswordValidation'
+export { default as MenuCountry } from './MenuCountry'
 
 export { SubmitRevision } from './submissionRevision'
 
diff --git a/packages/component-faraday-ui/src/modals/FormModal.js b/packages/component-faraday-ui/src/modals/FormModal.js
index e9adb3b78c837287a61182aefc121b1851bbad5d..e798227aac61a7a99b16813a0f56deec6fc3b117 100644
--- a/packages/component-faraday-ui/src/modals/FormModal.js
+++ b/packages/component-faraday-ui/src/modals/FormModal.js
@@ -19,12 +19,13 @@ import {
   Text,
   Item,
   Label,
+  MenuCountry,
   IconButton,
   RowOverrideAlert,
   ItemOverrideAlert,
 } from 'pubsweet-component-faraday-ui'
 
-const AddUserForm = ({ roles, countries, titles }) => (
+const AddUserForm = ({ roles, titles }) => (
   <Fragment>
     <Row alignItems="baseline" mb={1} mt={1}>
       <Item mr={1} vertical>
@@ -72,7 +73,7 @@ const AddUserForm = ({ roles, countries, titles }) => (
         <Label>Country</Label>
         <ValidatedField
           component={input => (
-            <Menu options={countries} {...input} placeholder="Please select" />
+            <MenuCountry {...input} placeholder="Please select" />
           )}
           name="country"
         />
@@ -88,7 +89,7 @@ const AddUserForm = ({ roles, countries, titles }) => (
   </Fragment>
 )
 
-const EditForm = ({ titles, countries }) => (
+const EditForm = ({ titles }) => (
   <Fragment>
     <Row alignItems="center" mb={2} mt={1}>
       <Item mr={1} vertical>
@@ -115,7 +116,7 @@ const EditForm = ({ titles, countries }) => (
         <Label>Country</Label>
         <ValidatedField
           component={input => (
-            <Menu options={countries} {...input} placeholder="Please select" />
+            <MenuCountry {...input} placeholder="Please select" />
           )}
           name="country"
         />
@@ -186,7 +187,6 @@ const FormModal = ({
   onClose,
   subtitle,
   onConfirm,
-  countries,
   modalError,
   isFetching,
   handleSubmit,
@@ -198,9 +198,9 @@ const FormModal = ({
     <H2>{title}</H2>
     {edit && <Text secondary>{user.email}</Text>}
     {edit ? (
-      <EditForm countries={countries} titles={titles} />
+      <EditForm titles={titles} />
     ) : (
-      <AddUserForm countries={countries} roles={roles} titles={titles} />
+      <AddUserForm roles={roles} titles={titles} />
     )}
     {modalError && (
       <Row mb={1}>
diff --git a/packages/component-invite/src/routes/collectionsInvitations/delete.js b/packages/component-invite/src/routes/collectionsInvitations/delete.js
index 3119458018dec661b6da2c2e20e3b9433aa7e3c1..c93402add18d6180d6f47bb688bd2b77fb7a1f4f 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/delete.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/delete.js
@@ -2,6 +2,7 @@ const config = require('config')
 
 const {
   Team,
+  Fragment,
   services,
   authsome: authsomeHelper,
 } = require('pubsweet-component-helper-service')
@@ -70,6 +71,7 @@ module.exports = models => async (req, res) => {
       const fragment = await FragmentModel.find(
         last(get(collection, 'fragments', [])),
       )
+      const fragmentHelper = new Fragment({ fragment })
 
       const fragmentId = fragment.id
       const teamHelperForFragment = new Team({
@@ -114,19 +116,57 @@ module.exports = models => async (req, res) => {
         await deleteFilesS3({ fileKeys, s3Config })
       }
 
+      let shouldAuthorBeNotified
+      if (fragment.invitations.length > 0) {
+        shouldAuthorBeNotified = true
+      }
+
+      const reviewers = [
+        ...(await fragmentHelper.getReviewers({
+          UserModel,
+          type: 'accepted',
+        })),
+        ...(await fragmentHelper.getReviewers({
+          UserModel,
+          type: 'submitted',
+        })),
+      ]
+
       fragment.invitations = []
       fragment.recommendations = []
       fragment.revision && delete fragment.revision
-      fragment.save()
-    }
+      await fragment.save()
 
-    notifications.sendInvitedHEEmail({
-      models,
-      collection,
-      invitedHE: user,
-      isCanceled: true,
-      baseUrl: services.getBaseUrl(req),
-    })
+      notifications.notifyInvitedHEWhenRemoved({
+        models,
+        collection,
+        invitedHE: user,
+        baseUrl: services.getBaseUrl(req),
+      })
+
+      notifications.notifyReviewersWhenHERemoved({
+        models,
+        collection,
+        reviewers,
+        baseUrl: services.getBaseUrl(req),
+      })
+
+      if (shouldAuthorBeNotified) {
+        notifications.notifyAuthorWhenHERemoved({
+          models,
+          collection,
+          baseUrl: services.getBaseUrl(req),
+        })
+      }
+    } else {
+      notifications.sendInvitedHEEmail({
+        models,
+        collection,
+        invitedHE: user,
+        isCanceled: true,
+        baseUrl: services.getBaseUrl(req),
+      })
+    }
 
     return res.status(200).json({})
   } catch (e) {
diff --git a/packages/component-invite/src/routes/collectionsInvitations/emails/emailCopy.js b/packages/component-invite/src/routes/collectionsInvitations/emails/emailCopy.js
index c7c665749f3f3bb53a1743c6843753186201620d..66a72b51a80df466da1511014dc702a0132b035b 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/emails/emailCopy.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/emails/emailCopy.js
@@ -1,6 +1,7 @@
 const config = require('config')
 
 const staffEmail = config.get('journal.staffEmail')
+const journalName = config.get('journal.name')
 
 const getEmailCopy = ({ emailType, titleText, targetUserName, comments }) => {
   let paragraph
@@ -34,6 +35,30 @@ const getEmailCopy = ({ emailType, titleText, targetUserName, comments }) => {
       paragraph = `${targetUserName} has removed you from the role of Handling Editor for ${titleText}.<br/><br/>
         The manuscript will no longer appear in your dashboard. Please contact ${staffEmail} if you have any questions about this change.`
       break
+    case 'author-he-removed':
+      hasIntro = true
+      hasLink = false
+      hasSignature = true
+      paragraph = `We had to replace the handling editor of your manuscript ${titleText}. We apologise for any inconvenience, but it was necessary in order to move your manuscript forward.<br/><br/>
+        If you have questions please email them to ${staffEmail}.<br/><br/>
+        Thank you for your submission to ${journalName}.`
+      break
+    case 'he-he-removed':
+      hasIntro = true
+      hasLink = false
+      hasSignature = true
+      paragraph = `The editor in chief removed you from the manuscript "${titleText}".<br/><br/>
+        If you have any questions regarding this action, please let us know at ${staffEmail}.<br/><br/>
+        Thank you for reviewing ${journalName}.`
+      break
+    case 'reviewer-he-removed':
+      hasIntro = true
+      hasLink = false
+      hasSignature = true
+      paragraph = `We had to replace the handling editor of the manuscript "${titleText}". We apologise for any inconvenience this may cause.<br/><br/>
+        If you have started the review process please email the content to ${staffEmail}.<br/><br/>
+        Thank you for reviewing ${journalName}.`
+      break
     default:
       throw new Error(`The ${emailType} email type is not defined.`)
   }
diff --git a/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js b/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js
index 2b85380e873f1a686ed362c33c0b991eabac3aef..d1378dca273dcb3be93426c1841c71f4685e085d 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js
@@ -35,9 +35,7 @@ module.exports = {
     } ${submittingAuthor.lastName}`
 
     const userHelper = new User({ UserModel })
-    const eics = await userHelper.getEditorsInChief()
-    const eic = eics[0]
-    const eicName = `${eic.firstName} ${eic.lastName}`
+    const eicName = await userHelper.getEiCName()
     const { customId } = collection
 
     const { paragraph, ...bodyProps } = getEmailCopy({
@@ -73,6 +71,136 @@ module.exports = {
 
     return email.sendEmail()
   },
+  notifyAuthorWhenHERemoved: async ({
+    baseUrl,
+    collection,
+    models: { User: UserModel, Fragment: FragmentModel },
+  }) => {
+    const fragmentId = last(collection.fragments)
+    const fragment = await FragmentModel.find(fragmentId)
+    const fragmentHelper = new Fragment({ fragment })
+    const { title: titleText } = await fragmentHelper.getFragmentData()
+    const { submittingAuthor } = await fragmentHelper.getAuthorData({
+      UserModel,
+    })
+
+    const userHelper = new User({ UserModel })
+    const eicName = await userHelper.getEiCName()
+    const { customId } = collection
+
+    const { paragraph, ...bodyProps } = getEmailCopy({
+      titleText,
+      emailType: 'author-he-removed',
+    })
+
+    const email = new Email({
+      type: 'user',
+      fromEmail: `${eicName} <${staffEmail}>`,
+      toUser: {
+        email: submittingAuthor.email,
+        name: `${submittingAuthor.lastName}`,
+      },
+      content: {
+        subject: `${customId}:  Your manuscript's editor was changed`,
+        paragraph,
+        signatureName: eicName,
+        signatureJournal: journalName,
+        unsubscribeLink: services.createUrl(baseUrl, unsubscribeSlug, {
+          id: submittingAuthor.id,
+          token: submittingAuthor.accessTokens.unsubscribe,
+        }),
+      },
+      bodyProps,
+    })
+
+    return email.sendEmail()
+  },
+  notifyInvitedHEWhenRemoved: async ({
+    baseUrl,
+    invitedHE,
+    collection,
+    models: { User: UserModel, Fragment: FragmentModel },
+  }) => {
+    const fragmentId = last(collection.fragments)
+    const fragment = await FragmentModel.find(fragmentId)
+    const fragmentHelper = new Fragment({ fragment })
+    const { title: titleText } = await fragmentHelper.getFragmentData()
+
+    const userHelper = new User({ UserModel })
+    const eicName = await userHelper.getEiCName()
+    const { customId } = collection
+
+    const { paragraph, ...bodyProps } = getEmailCopy({
+      titleText,
+      emailType: 'he-he-removed',
+    })
+
+    const email = new Email({
+      type: 'user',
+      fromEmail: `${eicName} <${staffEmail}>`,
+      toUser: {
+        email: invitedHE.email,
+        name: `${invitedHE.lastName}`,
+      },
+      content: {
+        subject: `${customId}: The editor in chief removed you from ${titleText}`,
+        paragraph,
+        signatureName: eicName,
+        signatureJournal: journalName,
+        unsubscribeLink: services.createUrl(baseUrl, unsubscribeSlug, {
+          id: invitedHE.id,
+          token: invitedHE.accessTokens.unsubscribe,
+        }),
+      },
+      bodyProps,
+    })
+
+    return email.sendEmail()
+  },
+  notifyReviewersWhenHERemoved: async ({
+    baseUrl,
+    collection,
+    reviewers,
+    models: { User: UserModel, Fragment: FragmentModel },
+  }) => {
+    const fragmentId = last(collection.fragments)
+    const fragment = await FragmentModel.find(fragmentId)
+    const fragmentHelper = new Fragment({ fragment })
+    const { title: titleText } = await fragmentHelper.getFragmentData()
+
+    const userHelper = new User({ UserModel })
+    const eicName = await userHelper.getEiCName()
+    const { customId } = collection
+
+    const { paragraph, ...bodyProps } = getEmailCopy({
+      titleText,
+      emailType: 'reviewer-he-removed',
+    })
+
+    reviewers.forEach(reviewer => {
+      const email = new Email({
+        type: 'user',
+        toUser: {
+          email: reviewer.email,
+          name: reviewer.lastName,
+        },
+        fromEmail: `${eicName} <${staffEmail}>`,
+        content: {
+          subject: `${customId}: The handling editor of a manuscript that you were reviewing was changed`,
+          paragraph,
+          signatureName: eicName,
+          signatureJournal: journalName,
+          unsubscribeLink: services.createUrl(baseUrl, unsubscribeSlug, {
+            id: reviewer.id,
+            token: reviewer.accessTokens.unsubscribe,
+          }),
+        },
+        bodyProps,
+      })
+
+      return email.sendEmail()
+    })
+  },
   sendEiCEmail: async ({
     reason,
     baseUrl,
diff --git a/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js b/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js
index dcbe3c22f7278a55af53a9e9477f80860ea4609f..c901c343b92e974cc532becf8b6c881411212e89 100644
--- a/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js
+++ b/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js
@@ -231,7 +231,7 @@ module.exports = {
           firstName: author.firstName,
           lastName: author.lastName,
           affiliation: author.affiliation,
-          title: author.title,
+          title: author.title.toLowerCase(),
           country: author.country,
         })
         email.content.ctaText = 'CONFIRM ACCOUNT'
diff --git a/packages/components-faraday/package.json b/packages/components-faraday/package.json
index ec2887fbaf640fa36eff33017b8d6fb46557e1ba..ba1165a34dbcfcd292dfa1ea7f9bfc5313435a46 100644
--- a/packages/components-faraday/package.json
+++ b/packages/components-faraday/package.json
@@ -10,7 +10,7 @@
     "country-list": "^1.1.0",
     "moment": "^2.22.1",
     "prop-types": "^15.5.10",
-    "pubsweet-component-login": "^1.2.0",
+    "pubsweet-component-login": "^1.2.2",
     "react": "^16.4.2",
     "react-dnd": "^2.5.4",
     "react-dnd-html5-backend": "^2.5.4",
diff --git a/packages/components-faraday/src/components/Admin/AddUser.js b/packages/components-faraday/src/components/Admin/AddUser.js
index 572e42f0755c5f0ae873b190c0d6864b1889139e..7fd409d679850f3e7418baa28af3f82bc6f361cc 100644
--- a/packages/components-faraday/src/components/Admin/AddUser.js
+++ b/packages/components-faraday/src/components/Admin/AddUser.js
@@ -9,7 +9,6 @@ import {
   IconButton,
   withRoles,
   withFetching,
-  withCountries,
 } from 'pubsweet-component-faraday-ui'
 
 const AddUser = ({ edit, journal, handleSubmit, ...rest }) => (
@@ -32,6 +31,4 @@ const AddUser = ({ edit, journal, handleSubmit, ...rest }) => (
   </OpenModal>
 )
 
-export default compose(withJournal, withCountries, withRoles, withFetching)(
-  AddUser,
-)
+export default compose(withJournal, withRoles, withFetching)(AddUser)
diff --git a/packages/components-faraday/src/components/Login/LoginPage.js b/packages/components-faraday/src/components/Login/LoginPage.js
index 55893a41090cf2a321853163a28edad365e6e8c1..329c6038d6814236ae137b8fe15e2e0fbde48cc5 100644
--- a/packages/components-faraday/src/components/Login/LoginPage.js
+++ b/packages/components-faraday/src/components/Login/LoginPage.js
@@ -14,11 +14,12 @@ import {
   Text,
   Label,
   ActionLink,
+  withFetching,
 } from 'pubsweet-component-faraday-ui'
 
 const PasswordField = input => <TextField {...input} type="password" />
 
-const Login = ({ handleSubmit, loginError }) => (
+const Login = ({ handleSubmit, fetchingError }) => (
   <Root onSubmit={handleSubmit}>
     <CustomH2>Login</CustomH2>
     <Row mt={3}>
@@ -53,9 +54,9 @@ const Login = ({ handleSubmit, loginError }) => (
       LOG IN
     </Button>
 
-    {loginError && (
+    {fetchingError && (
       <Row justify="flex-start" mt={1}>
-        <Text error>{loginError}</Text>
+        <Text error>{fetchingError}</Text>
       </Row>
     )}
 
@@ -71,14 +72,10 @@ const Login = ({ handleSubmit, loginError }) => (
 )
 
 const LoginPage = compose(
-  connect(
-    state => ({
-      loginError: state.error,
-    }),
-    {
-      logoutUser,
-    },
-  ),
+  withFetching,
+  connect(null, {
+    logoutUser,
+  }),
   withProps({ passwordReset: true }),
   lifecycle({
     componentDidMount() {
@@ -89,9 +86,9 @@ const LoginPage = compose(
   reduxForm({
     form: 'login',
     enableReinitialize: false,
-    onSubmit: (values, dispatch, { location }) => {
+    onSubmit: (values, dispatch, { location, setError }) => {
       const redirectTo = get(location, 'state.from.pathname', '/dashboard')
-      dispatch(loginUser(values, redirectTo))
+      dispatch(loginUser(values, redirectTo, setError))
     },
   }),
 )(Login)
diff --git a/packages/components-faraday/src/components/SignUp/SignUpStep0.js b/packages/components-faraday/src/components/SignUp/SignUpStep0.js
index 4cb4f094d06d1585a6a814e4cf1fda0af3b3d9bf..2baf1131f0e20c0cc42b149e0fdab343f7585cbf 100644
--- a/packages/components-faraday/src/components/SignUp/SignUpStep0.js
+++ b/packages/components-faraday/src/components/SignUp/SignUpStep0.js
@@ -11,8 +11,8 @@ import {
   Item,
   Label,
   ActionLink,
+  MenuCountry,
   ItemOverrideAlert,
-  withCountries,
 } from 'pubsweet-component-faraday-ui'
 
 const AgreeCheckbox = ({ value, onChange }) => (
@@ -27,14 +27,7 @@ const AgreeCheckbox = ({ value, onChange }) => (
   </Row>
 )
 
-const Step0 = ({
-  type,
-  error,
-  journal,
-  countries,
-  handleSubmit,
-  initialValues,
-}) =>
+const Step0 = ({ type, error, journal, handleSubmit, initialValues }) =>
   !isUndefined(initialValues) ? (
     <Fragment>
       <Row mb={2} mt={3}>
@@ -75,11 +68,7 @@ const Step0 = ({
           <Label required>Country</Label>
           <ValidatedField
             component={input => (
-              <Menu
-                {...input}
-                options={countries}
-                placeholder="Please select"
-              />
+              <MenuCountry {...input} placeholder="Please select" />
             )}
             name="country"
             validate={[requiredValidator]}
@@ -142,7 +131,6 @@ const Step0 = ({
   )
 
 export default compose(
-  withCountries,
   reduxForm({
     form: 'signUpInvitation',
     destroyOnUnmount: false,
diff --git a/packages/pubsweet-component-login/CHANGELOG.md b/packages/pubsweet-component-login/CHANGELOG.md
deleted file mode 100644
index 053b9a9ceb89f38e0ccc02a60386a493de816d58..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/CHANGELOG.md
+++ /dev/null
@@ -1,204 +0,0 @@
-# Change Log
-
-All notable changes to this project will be documented in this file.
-See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
-
-<a name="1.2.0"></a>
-# [1.2.0](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.18...pubsweet-component-login@1.2.0) (2018-11-05)
-
-
-### Features
-
-* GraphQL Login component ([70df3de](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/70df3de))
-* GraphQL Xpub submit component ([ba07060](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/ba07060))
-
-
-
-
-<a name="1.1.18"></a>
-## [1.1.18](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.17...pubsweet-component-login@1.1.18) (2018-10-08)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.17"></a>
-## [1.1.17](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.16...pubsweet-component-login@1.1.17) (2018-09-27)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.16"></a>
-## [1.1.16](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.15...pubsweet-component-login@1.1.16) (2018-09-19)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.15"></a>
-## [1.1.15](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.14...pubsweet-component-login@1.1.15) (2018-09-06)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.14"></a>
-## [1.1.14](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.13...pubsweet-component-login@1.1.14) (2018-09-04)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.13"></a>
-## [1.1.13](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.12...pubsweet-component-login@1.1.13) (2018-08-20)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.12"></a>
-## [1.1.12](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.11...pubsweet-component-login@1.1.12) (2018-08-17)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.11"></a>
-## [1.1.11](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.10...pubsweet-component-login@1.1.11) (2018-08-02)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.10"></a>
-## [1.1.10](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.9...pubsweet-component-login@1.1.10) (2018-07-27)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.9"></a>
-## [1.1.9](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.8...pubsweet-component-login@1.1.9) (2018-07-12)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.8"></a>
-## [1.1.8](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.7...pubsweet-component-login@1.1.8) (2018-07-09)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.7"></a>
-## [1.1.7](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.6...pubsweet-component-login@1.1.7) (2018-07-03)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.6"></a>
-## [1.1.6](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.5...pubsweet-component-login@1.1.6) (2018-07-02)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.5"></a>
-## [1.1.5](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.4...pubsweet-component-login@1.1.5) (2018-06-28)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.4"></a>
-## [1.1.4](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.3...pubsweet-component-login@1.1.4) (2018-06-28)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.3"></a>
-## [1.1.3](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.2...pubsweet-component-login@1.1.3) (2018-06-19)
-
-
-### Bug Fixes
-
-* **pubsweet-ui:** tests are failing ([0e57798](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/0e57798))
-
-
-
-
-<a name="1.1.2"></a>
-## [1.1.2](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.1...pubsweet-component-login@1.1.2) (2018-04-03)
-
-
-
-
-**Note:** Version bump only for package pubsweet-component-login
-
-<a name="1.1.1"></a>
-## [1.1.1](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.1.0...pubsweet-component-login@1.1.1) (2018-03-15)
-
-
-### Bug Fixes
-
-* **login:** add missing recompose dependency ([a3b5a80](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/a3b5a80)), closes [#353](https://gitlab.coko.foundation/pubsweet/pubsweet/issues/353)
-
-
-
-
-<a name="1.1.0"></a>
-# [1.1.0](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.0.1...pubsweet-component-login@1.1.0) (2018-03-05)
-
-
-### Bug Fixes
-
-* **components:** login example ([6dfd66c](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/6dfd66c))
-* **components:** login tests were failing after refactor ([62be047](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/62be047))
-* **components:** signup and login error examples ([3f991ec](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/3f991ec))
-
-
-### Features
-
-* **elife-theme:** add elife theme ([e406e0d](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/e406e0d))
-
-
-
-
-<a name="1.0.1"></a>
-
-## [1.0.1](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@1.0.0...pubsweet-component-login@1.0.1) (2018-02-08)
-
-### Bug Fixes
-
-* **components:** update react-router-redux version to match client ([3d257ef](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/3d257ef))
-
-<a name="1.0.0"></a>
-
-# [1.0.0](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-login@0.6.0...pubsweet-component-login@1.0.0) (2018-02-02)
-
-### Features
-
-* **client:** upgrade React to version 16 ([626cf59](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/626cf59)), closes [#65](https://gitlab.coko.foundation/pubsweet/pubsweet/issues/65)
-
-### BREAKING CHANGES
-
-* **client:** Upgrade React to version 16
diff --git a/packages/pubsweet-component-login/Login.jsx b/packages/pubsweet-component-login/Login.jsx
deleted file mode 100644
index 6cc5a2d8597fa53e053e4c5615f2502093e17280..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/Login.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import { Field } from 'formik'
-import { isEmpty } from 'lodash'
-import {
-  CenteredColumn,
-  ErrorText,
-  H1,
-  Link,
-  Button,
-  TextField,
-} from '@pubsweet/ui'
-import styled from 'styled-components'
-
-// These enable tests to select components
-const Signup = styled.div``
-const ResetPassword = styled.div``
-
-const UsernameInput = props => <TextField label="Username" {...props.field} />
-const PasswordInput = props => (
-  <TextField label="Password" {...props.field} type="password" />
-)
-
-const Login = ({
-  errors,
-  handleSubmit,
-  signup = true,
-  passwordReset = true,
-}) => (
-  <CenteredColumn small>
-    <H1>Login</H1>
-
-    {!isEmpty(errors) && <ErrorText>{errors}</ErrorText>}
-    <form onSubmit={handleSubmit}>
-      <Field component={UsernameInput} name="username" />
-      <Field component={PasswordInput} name="password" />
-      <Button primary type="submit">
-        Login
-      </Button>
-    </form>
-
-    {signup && (
-      <Signup>
-        <span>Don&apos;t have an account? </span>
-        <Link to="/signup">Sign up</Link>
-      </Signup>
-    )}
-
-    {passwordReset && (
-      <ResetPassword>
-        <span>Forgot your password? </span>
-        <Link to="/password-reset">Reset password</Link>
-      </ResetPassword>
-    )}
-  </CenteredColumn>
-)
-
-Login.propTypes = {
-  error: PropTypes.string,
-  actions: PropTypes.object,
-  location: PropTypes.object,
-  signup: PropTypes.bool,
-  passwordReset: PropTypes.bool,
-}
-
-// used by tests
-export { Login, ErrorText, Signup, ResetPassword }
-
-// used by consumers
-export default Login
diff --git a/packages/pubsweet-component-login/Login.md b/packages/pubsweet-component-login/Login.md
deleted file mode 100644
index 8efa765dd57c83028d3d4e6d9885797bac8245b7..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/Login.md
+++ /dev/null
@@ -1,40 +0,0 @@
-A login form
-
-```js
-const { withFormik } = require('formik')
-
-const LoginForm = withFormik({
-  initialValues: {
-    username: '',
-    password: '',
-  },
-  mapPropsToValues: props => ({
-    username: props.username,
-    password: props.password,
-  }),
-  displayName: 'login',
-  handleSubmit: val => console.log(val),
-})(Login)
-;<LoginForm />
-```
-
-Which can have an error message:
-
-```js
-const { withFormik } = require('formik')
-
-const LoginForm = withFormik({
-  initialValues: {
-    username: '',
-    password: '',
-  },
-  mapPropsToValues: props => ({
-    username: props.username,
-    password: props.password,
-  }),
-  displayName: 'login',
-  handleSubmit: (values, { setErrors }) =>
-    setErrors('Wrong username or password.'),
-})(Login)
-;<LoginForm />
-```
diff --git a/packages/pubsweet-component-login/Login.test.jsx b/packages/pubsweet-component-login/Login.test.jsx
deleted file mode 100644
index f696b5eaa420beee8fb6fdf3311461b1ed28f97a..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/Login.test.jsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { shallow } from 'enzyme'
-import React from 'react'
-
-import { Login, ErrorText, Signup, ResetPassword } from './Login'
-
-describe('<Login/>', () => {
-  const makeWrapper = (props = {}) => shallow(<Login {...props} />)
-
-  it('renders the login form', () => {
-    expect(makeWrapper()).toMatchSnapshot()
-  })
-
-  it('shows error', () => {
-    const wrapper = makeWrapper({ errors: 'Yikes!' })
-    expect(wrapper.find(ErrorText)).toHaveLength(1)
-  })
-
-  it('can hide sign up link', () => {
-    const wrapper1 = makeWrapper()
-    const wrapper2 = makeWrapper({ signup: false })
-    expect(wrapper1.find(Signup)).toHaveLength(1)
-    expect(wrapper2.find(Signup)).toHaveLength(0)
-  })
-
-  it('can hide password reset link', () => {
-    const wrapper1 = makeWrapper()
-    const wrapper2 = makeWrapper({ passwordReset: false })
-    expect(wrapper1.find(ResetPassword)).toHaveLength(1)
-    expect(wrapper2.find(ResetPassword)).toHaveLength(0)
-  })
-
-  it('triggers submit handler', () => {
-    const handleSubmit = jest.fn()
-    const wrapper = makeWrapper({ handleSubmit })
-    wrapper.find('form').simulate('submit')
-    expect(handleSubmit).toHaveBeenCalled()
-  })
-})
diff --git a/packages/pubsweet-component-login/LoginContainer.js b/packages/pubsweet-component-login/LoginContainer.js
deleted file mode 100644
index 250dd2d75b9adeb1686805b4a9d8ecd7d8b4291b..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/LoginContainer.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { withFormik } from 'formik'
-import { compose } from 'recompose'
-import { connect } from 'react-redux'
-import { loginUser } from './actions'
-
-import Login from './Login'
-import redirectPath from './redirect'
-
-const handleSubmit = (values, { props: { dispatch, location }, setErrors }) => {
-  dispatch(loginUser(values, redirectPath({ location }), setErrors))
-}
-
-const enhancedFormik = withFormik({
-  initialValues: {
-    username: '',
-    password: '',
-  },
-  mapPropsToValues: props => ({
-    username: props.username,
-    password: props.password,
-  }),
-  displayName: 'login',
-  handleSubmit,
-})(Login)
-
-export default compose(connect(state => state))(enhancedFormik)
diff --git a/packages/pubsweet-component-login/__snapshots__/Login.test.jsx.snap b/packages/pubsweet-component-login/__snapshots__/Login.test.jsx.snap
deleted file mode 100644
index c37d2096241bcb88782ac550b9bd8c8f121e1e52..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/__snapshots__/Login.test.jsx.snap
+++ /dev/null
@@ -1,473 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`<Login/> renders the login form 1`] = `
-ShallowWrapper {
-  Symbol(enzyme.__root__): [Circular],
-  Symbol(enzyme.__unrendered__): <Login />,
-  Symbol(enzyme.__renderer__): Object {
-    "batchedUpdates": [Function],
-    "getNode": [Function],
-    "render": [Function],
-    "simulateError": [Function],
-    "simulateEvent": [Function],
-    "unmount": [Function],
-  },
-  Symbol(enzyme.__node__): Object {
-    "instance": null,
-    "key": undefined,
-    "nodeType": "class",
-    "props": Object {
-      "children": Array [
-        <styled.h1>
-          Login
-        </styled.h1>,
-        false,
-        <form>
-          <C
-            component={[Function]}
-            name="username"
-          />
-          <C
-            component={[Function]}
-            name="password"
-          />
-          <styled.button
-            primary={true}
-            type="submit"
-          >
-            Login
-          </styled.button>
-        </form>,
-        <styled.div>
-          <span>
-            Don't have an account? 
-          </span>
-          <Styled(Link)
-            to="/signup"
-          >
-            Sign up
-          </Styled(Link)>
-        </styled.div>,
-        <styled.div>
-          <span>
-            Forgot your password? 
-          </span>
-          <Styled(Link)
-            to="/password-reset"
-          >
-            Reset password
-          </Styled(Link)>
-        </styled.div>,
-      ],
-      "small": true,
-    },
-    "ref": null,
-    "rendered": Array [
-      Object {
-        "instance": null,
-        "key": undefined,
-        "nodeType": "class",
-        "props": Object {
-          "children": "Login",
-        },
-        "ref": null,
-        "rendered": "Login",
-        "type": [Function],
-      },
-      false,
-      Object {
-        "instance": null,
-        "key": undefined,
-        "nodeType": "host",
-        "props": Object {
-          "children": Array [
-            <C
-              component={[Function]}
-              name="username"
-            />,
-            <C
-              component={[Function]}
-              name="password"
-            />,
-            <styled.button
-              primary={true}
-              type="submit"
-            >
-              Login
-            </styled.button>,
-          ],
-          "onSubmit": undefined,
-        },
-        "ref": null,
-        "rendered": Array [
-          Object {
-            "instance": null,
-            "key": undefined,
-            "nodeType": "function",
-            "props": Object {
-              "component": [Function],
-              "name": "username",
-            },
-            "ref": null,
-            "rendered": null,
-            "type": [Function],
-          },
-          Object {
-            "instance": null,
-            "key": undefined,
-            "nodeType": "function",
-            "props": Object {
-              "component": [Function],
-              "name": "password",
-            },
-            "ref": null,
-            "rendered": null,
-            "type": [Function],
-          },
-          Object {
-            "instance": null,
-            "key": undefined,
-            "nodeType": "class",
-            "props": Object {
-              "children": "Login",
-              "primary": true,
-              "type": "submit",
-            },
-            "ref": null,
-            "rendered": "Login",
-            "type": [Function],
-          },
-        ],
-        "type": "form",
-      },
-      Object {
-        "instance": null,
-        "key": undefined,
-        "nodeType": "class",
-        "props": Object {
-          "children": Array [
-            <span>
-              Don't have an account? 
-            </span>,
-            <Styled(Link)
-              to="/signup"
-            >
-              Sign up
-            </Styled(Link)>,
-          ],
-        },
-        "ref": null,
-        "rendered": Array [
-          Object {
-            "instance": null,
-            "key": undefined,
-            "nodeType": "host",
-            "props": Object {
-              "children": "Don't have an account? ",
-            },
-            "ref": null,
-            "rendered": "Don't have an account? ",
-            "type": "span",
-          },
-          Object {
-            "instance": null,
-            "key": undefined,
-            "nodeType": "class",
-            "props": Object {
-              "children": "Sign up",
-              "to": "/signup",
-            },
-            "ref": null,
-            "rendered": "Sign up",
-            "type": [Function],
-          },
-        ],
-        "type": [Function],
-      },
-      Object {
-        "instance": null,
-        "key": undefined,
-        "nodeType": "class",
-        "props": Object {
-          "children": Array [
-            <span>
-              Forgot your password? 
-            </span>,
-            <Styled(Link)
-              to="/password-reset"
-            >
-              Reset password
-            </Styled(Link)>,
-          ],
-        },
-        "ref": null,
-        "rendered": Array [
-          Object {
-            "instance": null,
-            "key": undefined,
-            "nodeType": "host",
-            "props": Object {
-              "children": "Forgot your password? ",
-            },
-            "ref": null,
-            "rendered": "Forgot your password? ",
-            "type": "span",
-          },
-          Object {
-            "instance": null,
-            "key": undefined,
-            "nodeType": "class",
-            "props": Object {
-              "children": "Reset password",
-              "to": "/password-reset",
-            },
-            "ref": null,
-            "rendered": "Reset password",
-            "type": [Function],
-          },
-        ],
-        "type": [Function],
-      },
-    ],
-    "type": [Function],
-  },
-  Symbol(enzyme.__nodes__): Array [
-    Object {
-      "instance": null,
-      "key": undefined,
-      "nodeType": "class",
-      "props": Object {
-        "children": Array [
-          <styled.h1>
-            Login
-          </styled.h1>,
-          false,
-          <form>
-            <C
-              component={[Function]}
-              name="username"
-            />
-            <C
-              component={[Function]}
-              name="password"
-            />
-            <styled.button
-              primary={true}
-              type="submit"
-            >
-              Login
-            </styled.button>
-          </form>,
-          <styled.div>
-            <span>
-              Don't have an account? 
-            </span>
-            <Styled(Link)
-              to="/signup"
-            >
-              Sign up
-            </Styled(Link)>
-          </styled.div>,
-          <styled.div>
-            <span>
-              Forgot your password? 
-            </span>
-            <Styled(Link)
-              to="/password-reset"
-            >
-              Reset password
-            </Styled(Link)>
-          </styled.div>,
-        ],
-        "small": true,
-      },
-      "ref": null,
-      "rendered": Array [
-        Object {
-          "instance": null,
-          "key": undefined,
-          "nodeType": "class",
-          "props": Object {
-            "children": "Login",
-          },
-          "ref": null,
-          "rendered": "Login",
-          "type": [Function],
-        },
-        false,
-        Object {
-          "instance": null,
-          "key": undefined,
-          "nodeType": "host",
-          "props": Object {
-            "children": Array [
-              <C
-                component={[Function]}
-                name="username"
-              />,
-              <C
-                component={[Function]}
-                name="password"
-              />,
-              <styled.button
-                primary={true}
-                type="submit"
-              >
-                Login
-              </styled.button>,
-            ],
-            "onSubmit": undefined,
-          },
-          "ref": null,
-          "rendered": Array [
-            Object {
-              "instance": null,
-              "key": undefined,
-              "nodeType": "function",
-              "props": Object {
-                "component": [Function],
-                "name": "username",
-              },
-              "ref": null,
-              "rendered": null,
-              "type": [Function],
-            },
-            Object {
-              "instance": null,
-              "key": undefined,
-              "nodeType": "function",
-              "props": Object {
-                "component": [Function],
-                "name": "password",
-              },
-              "ref": null,
-              "rendered": null,
-              "type": [Function],
-            },
-            Object {
-              "instance": null,
-              "key": undefined,
-              "nodeType": "class",
-              "props": Object {
-                "children": "Login",
-                "primary": true,
-                "type": "submit",
-              },
-              "ref": null,
-              "rendered": "Login",
-              "type": [Function],
-            },
-          ],
-          "type": "form",
-        },
-        Object {
-          "instance": null,
-          "key": undefined,
-          "nodeType": "class",
-          "props": Object {
-            "children": Array [
-              <span>
-                Don't have an account? 
-              </span>,
-              <Styled(Link)
-                to="/signup"
-              >
-                Sign up
-              </Styled(Link)>,
-            ],
-          },
-          "ref": null,
-          "rendered": Array [
-            Object {
-              "instance": null,
-              "key": undefined,
-              "nodeType": "host",
-              "props": Object {
-                "children": "Don't have an account? ",
-              },
-              "ref": null,
-              "rendered": "Don't have an account? ",
-              "type": "span",
-            },
-            Object {
-              "instance": null,
-              "key": undefined,
-              "nodeType": "class",
-              "props": Object {
-                "children": "Sign up",
-                "to": "/signup",
-              },
-              "ref": null,
-              "rendered": "Sign up",
-              "type": [Function],
-            },
-          ],
-          "type": [Function],
-        },
-        Object {
-          "instance": null,
-          "key": undefined,
-          "nodeType": "class",
-          "props": Object {
-            "children": Array [
-              <span>
-                Forgot your password? 
-              </span>,
-              <Styled(Link)
-                to="/password-reset"
-              >
-                Reset password
-              </Styled(Link)>,
-            ],
-          },
-          "ref": null,
-          "rendered": Array [
-            Object {
-              "instance": null,
-              "key": undefined,
-              "nodeType": "host",
-              "props": Object {
-                "children": "Forgot your password? ",
-              },
-              "ref": null,
-              "rendered": "Forgot your password? ",
-              "type": "span",
-            },
-            Object {
-              "instance": null,
-              "key": undefined,
-              "nodeType": "class",
-              "props": Object {
-                "children": "Reset password",
-                "to": "/password-reset",
-              },
-              "ref": null,
-              "rendered": "Reset password",
-              "type": [Function],
-            },
-          ],
-          "type": [Function],
-        },
-      ],
-      "type": [Function],
-    },
-  ],
-  Symbol(enzyme.__options__): Object {
-    "adapter": ReactSixteenAdapter {
-      "options": Object {
-        "enableComponentDidUpdateOnSetState": true,
-        "lifecycles": Object {
-          "componentDidUpdate": Object {
-            "onSetState": true,
-          },
-          "getDerivedStateFromProps": true,
-          "getSnapshotBeforeUpdate": true,
-          "setState": Object {
-            "skipsComponentDidUpdateOnNullish": true,
-          },
-        },
-      },
-    },
-  },
-}
-`;
diff --git a/packages/pubsweet-component-login/actions.js b/packages/pubsweet-component-login/actions.js
deleted file mode 100644
index e74909742d021bb2f66d46674e968b5a3eed2919..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/actions.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import * as api from 'pubsweet-client/src/helpers/api'
-import {
-  LOGIN_REQUEST,
-  LOGIN_SUCCESS,
-  LOGIN_FAILURE,
-  LOGOUT_SUCCESS,
-  LOGOUT_REQUEST,
-} from 'pubsweet-client/src/actions/types'
-
-import { push } from 'react-router-redux'
-
-// TODO: This will break when rendered on a server
-const localStorage = window.localStorage || undefined
-
-// There are three possible states for our login
-// process and we need actions for each of them
-function loginRequest(credentials) {
-  return {
-    type: LOGIN_REQUEST,
-    credentials,
-  }
-}
-
-function loginSuccess(user) {
-  return {
-    type: LOGIN_SUCCESS,
-    token: user.token,
-    user,
-  }
-}
-
-function loginFailure(message) {
-  return {
-    type: LOGIN_FAILURE,
-    error: message,
-  }
-}
-
-// Calls the API to get a token and
-// dispatches actions along the way
-export function loginUser(credentials, redirectTo, setErrors) {
-  return dispatch => {
-    dispatch(loginRequest(credentials))
-    return api.create('/users/authenticate', credentials).then(
-      user => {
-        localStorage.setItem('token', user.token)
-        dispatch(loginSuccess(user))
-        if (redirectTo) dispatch(push(redirectTo))
-      },
-      err => {
-        setErrors(JSON.parse(err.response).message)
-        dispatch(loginFailure(err))
-      },
-    )
-  }
-}
-
-function logoutRequest() {
-  return {
-    type: LOGOUT_REQUEST,
-    isFetching: true,
-    isAuthenticated: true,
-  }
-}
-
-function logoutSuccess() {
-  return {
-    type: LOGOUT_SUCCESS,
-    isFetching: false,
-    isAuthenticated: false,
-  }
-}
-
-// Logs the user out
-// Since we are using JWTs, we just need to remove the token
-// from localStorage.
-export function logoutUser(redirectTo) {
-  return dispatch => {
-    dispatch(logoutRequest())
-    localStorage.removeItem('token')
-    dispatch(logoutSuccess())
-    if (redirectTo) dispatch(push(redirectTo))
-  }
-}
diff --git a/packages/pubsweet-component-login/graphql/LoginContainer.js b/packages/pubsweet-component-login/graphql/LoginContainer.js
deleted file mode 100644
index 83715d8019cbd8eb24bf086d9b7ebdb733daf80e..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/graphql/LoginContainer.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { compose } from 'recompose'
-import { withFormik } from 'formik'
-import { graphql } from 'react-apollo'
-
-import mutations from './mutations'
-import Login from '../Login'
-import redirectPath from '../redirect'
-
-const localStorage = window.localStorage || undefined
-
-const handleSubmit = (values, { props, setSubmitting, setErrors }) =>
-  props
-    .loginUser({ variables: { input: values } })
-    .then(({ data, errors }) => {
-      if (!errors) {
-        localStorage.setItem('token', data.loginUser.token)
-        props.history.push(redirectPath({ location: props.location }))
-        setSubmitting(true)
-      }
-    })
-    .catch(e => {
-      if (e.graphQLErrors) {
-        setSubmitting(false)
-        setErrors(e.graphQLErrors[0].message)
-      }
-    })
-
-const enhancedFormik = withFormik({
-  initialValues: {
-    username: '',
-    password: '',
-  },
-  mapPropsToValues: props => ({
-    username: props.username,
-    password: props.password,
-  }),
-  displayName: 'login',
-  handleSubmit,
-})(Login)
-
-export default compose(graphql(mutations.LOGIN_USER, { name: 'loginUser' }))(
-  enhancedFormik,
-)
diff --git a/packages/pubsweet-component-login/graphql/LoginContainer.test.jsx b/packages/pubsweet-component-login/graphql/LoginContainer.test.jsx
deleted file mode 100644
index 7df5518babca59528ec37fa596338e687f192365..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/graphql/LoginContainer.test.jsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import React from 'react'
-import { mount } from 'enzyme'
-import { MockedProvider } from 'react-apollo/test-utils'
-import { ThemeProvider } from 'styled-components'
-import { MemoryRouter, Route } from 'react-router-dom'
-
-import wait from 'waait'
-import { LOGIN_USER } from './mutations'
-import LoginContainer from './LoginContainer'
-
-const user1 = {
-  id: 'user1',
-  username: 'admin',
-  password: 'adminadmin',
-  admin: true,
-  teams: [],
-}
-
-const mocks = currentUser => [
-  {
-    request: {
-      query: LOGIN_USER,
-      variables: {
-        input: {
-          username: currentUser.username,
-          password: currentUser.password,
-        },
-      },
-    },
-    result: {
-      data: {
-        loginUser: {
-          user: currentUser,
-          token: 'greatToken',
-        },
-      },
-    },
-  },
-  {
-    request: {
-      query: LOGIN_USER,
-      variables: {
-        input: {
-          username: currentUser.username,
-          password: 'wrongPassword',
-        },
-      },
-    },
-    result: {
-      data: { loginUser: null },
-      errors: [{ message: 'Wrong username or password.' }],
-    },
-  },
-]
-
-let globalLocation
-
-function makeDeepWrapper(currentUser, props = {}) {
-  // A theme is needed because some components use colors
-  // specified in the theme to render themselves (e.g. Link)
-  const theme = {
-    colorPrimary: '#fff',
-    colorSecondary: '#fff',
-  }
-
-  return mount(
-    <ThemeProvider theme={theme}>
-      <MockedProvider addTypename={false} mocks={mocks(currentUser)}>
-        <MemoryRouter initialEntries={['/login']}>
-          <Route
-            {...props}
-            render={p => {
-              globalLocation = p.location
-              return <LoginContainer {...p} />
-            }}
-          />
-        </MemoryRouter>
-      </MockedProvider>
-    </ThemeProvider>,
-  )
-}
-
-describe('LoginContainer', () => {
-  beforeEach(() => {
-    window.localStorage.clear()
-    globalLocation = undefined
-  })
-
-  it('renders the login form', () => {
-    const wrapper = makeDeepWrapper(user1)
-    wrapper.update()
-
-    const fields = wrapper.find('Login')
-    expect(fields).toHaveLength(1)
-  })
-
-  it('submits login information and logs the user in', async () => {
-    const wrapper = makeDeepWrapper(user1)
-    wrapper.update()
-
-    const usernameField = wrapper.find('TextField[label="Username"] input')
-    usernameField.getDOMNode().value = user1.username
-    usernameField.simulate('change')
-    const passwordField = wrapper.find('TextField[label="Password"] input')
-    passwordField.getDOMNode().value = user1.password
-    passwordField.simulate('change')
-
-    wrapper.update()
-    const button = wrapper.find('button')
-    button.simulate('submit')
-
-    wrapper.update()
-    await wait(50)
-
-    expect(window.localStorage.token).toEqual('greatToken')
-    expect(globalLocation.pathname).toEqual('/testRedirect')
-  })
-
-  it('does not log in user with incorrect credentials', async () => {
-    const wrapper = makeDeepWrapper(user1)
-    wrapper.update()
-
-    const usernameField = wrapper.find('TextField[label="Username"] input')
-    usernameField.getDOMNode().value = user1.username
-    usernameField.simulate('change')
-    const passwordField = wrapper.find('TextField[label="Password"] input')
-    passwordField.getDOMNode().value = 'wrongPassword'
-    passwordField.simulate('change')
-
-    wrapper.update()
-    const button = wrapper.find('button')
-
-    button.simulate('submit')
-
-    wrapper.update()
-    await wait(50)
-
-    expect(wrapper.find('Login').text()).toContain(
-      'Wrong username or password.',
-    )
-    expect(window.localStorage.token).toEqual(undefined)
-    expect(globalLocation.pathname).toEqual('/login')
-  })
-})
diff --git a/packages/pubsweet-component-login/graphql/mutations/index.js b/packages/pubsweet-component-login/graphql/mutations/index.js
deleted file mode 100644
index ee44b493da283da233533820994539bc15529084..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/graphql/mutations/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import gql from 'graphql-tag'
-
-const LOGIN_USER = gql`
-  mutation($input: LoginUserInput) {
-    loginUser(input: $input) {
-      token
-    }
-  }
-`
-
-module.exports = {
-  LOGIN_USER,
-}
diff --git a/packages/pubsweet-component-login/index.js b/packages/pubsweet-component-login/index.js
deleted file mode 100644
index 155554e77ad3dfe5c27ec6c2d4b5b623dcb3d135..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-module.exports = {
-  frontend: {
-    components: [() => require('./LoginContainer')],
-    actions: () => require('./actions'),
-    reducers: () => require('./reducers'),
-  },
-}
diff --git a/packages/pubsweet-component-login/package.json b/packages/pubsweet-component-login/package.json
deleted file mode 100644
index 66896d9c43c7e256d5522d2e692e2d92e58a2654..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/package.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  "name": "pubsweet-component-login",
-  "version": "1.2.0",
-  "description": "Basic login component for PubSweet",
-  "main": "index.js",
-  "author": "Collaborative Knowledge Foundation",
-  "license": "MIT",
-  "dependencies": {
-    "@pubsweet/ui": "^8.8.0",
-    "formik": "1.3.0",
-    "prop-types": "^15.5.10",
-    "react-redux": "^5.0.6",
-    "react-router-dom": "^4.2.2",
-    "react-router-redux": "^5.0.0-alpha.9",
-    "recompose": "^0.26.0"
-  },
-  "peerDependencies": {
-    "pubsweet-client": ">=1.0.0",
-    "react": ">=15"
-  },
-  "repository": {
-    "type": "git",
-    "url": "https://gitlab.coko.foundation/pubsweet/pubsweet",
-    "path": "Login"
-  }
-}
diff --git a/packages/pubsweet-component-login/redirect.js b/packages/pubsweet-component-login/redirect.js
deleted file mode 100644
index 53d58f1e3e4d1fe5a5d53584048d7a389c340887..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/redirect.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { get } from 'lodash'
-import config from 'config'
-
-const allowedRedirect = pathname =>
-  !['/logout', '/login', '/signup'].includes(pathname)
-
-export default ({ location: { state } }) =>
-  state && state.from && allowedRedirect(state.from.pathname)
-    ? state.from.pathname
-    : get(config, 'pubsweet-client.login-redirect', '/')
diff --git a/packages/pubsweet-component-login/reducers.js b/packages/pubsweet-component-login/reducers.js
deleted file mode 100644
index d1f637a01e310491d842ac045d78ac1a0a879527..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/reducers.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import {
-  LOGIN_REQUEST,
-  LOGIN_SUCCESS,
-  LOGIN_FAILURE,
-  LOGOUT_SUCCESS,
-  LOGOUT_REQUEST,
-} from 'pubsweet-client/src/actions/types'
-
-// TODO: This will break when rendered on a server
-const localStorage = window.localStorage || undefined
-
-export default function userLogin(
-  state = {
-    isFetching: false,
-    isAuthenticated: false,
-    token: localStorage.getItem('token'),
-  },
-  action,
-) {
-  switch (action.type) {
-    case LOGIN_REQUEST:
-      return {
-        ...state,
-        isFetching: true,
-        isAuthenticated: false,
-        username: action.credentials.username,
-      }
-    case LOGIN_SUCCESS:
-      return {
-        ...state,
-        isFetching: false,
-        isAuthenticated: true,
-        user: action.user,
-        token: action.token,
-      }
-    case LOGIN_FAILURE:
-      return {
-        ...state,
-        isFetching: false,
-        isAuthenticated: false,
-        error: action.error,
-      }
-    case LOGOUT_SUCCESS:
-      return {
-        ...state,
-        isFetching: false,
-        isAuthenticated: false,
-      }
-    case LOGOUT_REQUEST:
-      return {
-        ...state,
-        isFetching: false,
-        isAuthenticated: false,
-      }
-    default:
-      return state
-  }
-}
diff --git a/packages/pubsweet-component-login/reducers.test.js b/packages/pubsweet-component-login/reducers.test.js
deleted file mode 100644
index a3733cf35bff6ae0ac033e66fdec726ce557d509..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/reducers.test.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import {
-  LOGIN_FAILURE,
-  LOGIN_REQUEST,
-  LOGIN_SUCCESS,
-  LOGOUT_REQUEST,
-  LOGOUT_SUCCESS,
-} from 'pubsweet-client/src/actions/types'
-
-jest.spyOn(Storage.prototype, 'getItem').mockImplementation(() => undefined)
-
-const reducer = require('./reducers').default
-
-describe('Login reducer', () => {
-  it('returns initial state', () => {
-    const newState = reducer(undefined, {})
-    expect(newState).toEqual({
-      isFetching: false,
-      isAuthenticated: false,
-      token: undefined,
-    })
-  })
-
-  it('stores username on login request', () => {
-    const action = {
-      type: LOGIN_REQUEST,
-      credentials: { username: 'milo minderbinder' },
-    }
-    const newState = reducer(undefined, action)
-    expect(newState).toMatchObject({
-      isFetching: true,
-      username: 'milo minderbinder',
-    })
-  })
-
-  it('stores user and token on login success', () => {
-    const action = {
-      type: LOGIN_SUCCESS,
-      user: { username: 'nurse duckett' },
-      token: 't0k3n',
-    }
-    const newState = reducer(undefined, action)
-    expect(newState).toMatchObject({
-      isAuthenticated: true,
-      user: action.user,
-      token: action.token,
-    })
-  })
-
-  it('stores error on login failure', () => {
-    const action = { type: LOGIN_FAILURE, error: new Error('Flies in eyes') }
-    const newState = reducer({ isAuthenticated: true }, action)
-    expect(newState).toMatchObject({
-      isAuthenticated: false,
-      error: action.error,
-    })
-  })
-
-  it('logs out on request', () => {
-    const action = { type: LOGOUT_REQUEST }
-    const newState = reducer({ isAuthenticated: true }, action)
-    expect(newState).toMatchObject({
-      isAuthenticated: false,
-    })
-  })
-
-  it('logs out on logout success', () => {
-    const action = { type: LOGOUT_SUCCESS }
-    const newState = reducer({ isAuthenticated: true }, action)
-    expect(newState).toMatchObject({
-      isAuthenticated: false,
-    })
-  })
-})
diff --git a/packages/pubsweet-component-login/types.js b/packages/pubsweet-component-login/types.js
deleted file mode 100644
index 3b66211c7193bd42a3e8aa9f3c63b46a57172a43..0000000000000000000000000000000000000000
--- a/packages/pubsweet-component-login/types.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export const LOGIN_REQUEST = 'LOGIN_REQUEST'
-export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
-export const LOGIN_FAILURE = 'LOGIN_FAILURE'
-
-export const LOGOUT_REQUEST = 'LOGOUT_REQUEST'
-export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'
-export const LOGOUT_FAILURE = 'LOGOUT_FAILURE'
diff --git a/yarn.lock b/yarn.lock
index 5ac69d613ac6686ad0d572b7fe2b838b917053cf..2e7a18b322fcc713ec832cef4529becf383b3dda 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -20,6 +20,13 @@
     source-map "^0.5.0"
     trim-right "^1.0.1"
 
+"@babel/helper-annotate-as-pure@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32"
+  integrity sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==
+  dependencies:
+    "@babel/types" "^7.0.0"
+
 "@babel/helper-function-name@7.0.0-beta.40":
   version "7.0.0-beta.40"
   resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.40.tgz#9d033341ab16517f40d43a73f2d81fc431ccd7b6"
@@ -102,6 +109,15 @@
     lodash "^4.2.0"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.0.0":
+  version "7.1.6"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.1.6.tgz#0adb330c3a281348a190263aceb540e10f04bcce"
+  integrity sha512-DMiUzlY9DSjVsOylJssxLHSgj6tWM9PRFJOGW/RaOglVOK9nzTxoOMfTfRQXGUCUQ/HmlG2efwC+XqUEJ5ay4w==
+  dependencies:
+    esutils "^2.0.2"
+    lodash "^4.17.10"
+    to-fast-properties "^2.0.0"
+
 "@elifesciences/elife-theme@^1.0.0":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@elifesciences/elife-theme/-/elife-theme-1.0.1.tgz#e112a36a5877e2ec17382b75f9d59563dc16f313"
@@ -110,6 +126,23 @@
     "@pubsweet/ui-toolkit" "^1.1.2"
     styled-components "^3.2.5"
 
+"@emotion/is-prop-valid@^0.6.8":
+  version "0.6.8"
+  resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.6.8.tgz#68ad02831da41213a2089d2cab4e8ac8b30cbd85"
+  integrity sha512-IMSL7ekYhmFlILXcouA6ket3vV7u9BqStlXzbKOF9HBtpUPMMlHU+bBxrLOa2NvleVwNIxeq/zL8LafLbeUXcA==
+  dependencies:
+    "@emotion/memoize" "^0.6.6"
+
+"@emotion/memoize@^0.6.6":
+  version "0.6.6"
+  resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.6.6.tgz#004b98298d04c7ca3b4f50ca2035d4f60d2eed1b"
+  integrity sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ==
+
+"@emotion/unitless@^0.7.0":
+  version "0.7.3"
+  resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.3.tgz#6310a047f12d21a1036fb031317219892440416f"
+  integrity sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg==
+
 "@icetee/ftp@^0.3.15":
   version "0.3.15"
   resolved "https://registry.yarnpkg.com/@icetee/ftp/-/ftp-0.3.15.tgz#d32efd91ab7585f0a3b6cbed9ceffe2763b04ec6"
@@ -203,6 +236,15 @@
     lodash "^4.17.4"
     styled-components "^3.2.5"
 
+"@pubsweet/ui-toolkit@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@pubsweet/ui-toolkit/-/ui-toolkit-2.0.1.tgz#d500cd808125909d812aafa8b77732d4a96b4433"
+  integrity sha512-mAhBSLte7NhHHkrA5pYyRoDpXLlam11T8USsYGRRtWyho2W4DkrQb4pl+KHXUhcR/O8Sgd1FIeGtrRfPUBdoUw==
+  dependencies:
+    color "^3.0.0"
+    lodash "^4.17.4"
+    styled-components "^4.1.1"
+
 "@pubsweet/ui-toolkit@latest":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@pubsweet/ui-toolkit/-/ui-toolkit-1.0.0.tgz#df05b54e7bbfabcb10c7afc2991752e1087d2298"
@@ -262,12 +304,12 @@
     redux-form "^7.0.3"
     styled-components "^3.2.5"
 
-"@pubsweet/ui@^8.8.0":
-  version "8.8.0"
-  resolved "https://registry.yarnpkg.com/@pubsweet/ui/-/ui-8.8.0.tgz#d6a845cd6d0d51c1c14956dccc11900fb87d2178"
-  integrity sha512-Ypr86pfeysF90upV7Ybk5vTr3uCnR0WWlfQZyXsmGn8jFyTlkHMJDNMw1XggfKxdmefyk5hkpcV4YiKklZ0+PA==
+"@pubsweet/ui@^9.0.1":
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/@pubsweet/ui/-/ui-9.0.1.tgz#263feecd28b5cd87f8e1ac14a69605a30a21457c"
+  integrity sha512-RQfYF2zLRuinBBHtuGvegnIjc5kj1IzHfFXKCcd219+byj6RH4EQTtThn65jBsN/IDD6mldPwN+HkYWPnI8Pyg==
   dependencies:
-    "@pubsweet/ui-toolkit" "^1.2.0"
+    "@pubsweet/ui-toolkit" "^2.0.1"
     babel-jest "^21.2.0"
     classnames "^2.2.5"
     enzyme "^3.7.0"
@@ -285,7 +327,7 @@
     recompose "^0.26.0"
     redux "^3.6.0"
     redux-form "^7.0.3"
-    styled-components "^3.2.5"
+    styled-components "^4.1.1"
 
 "@types/async@2.0.49":
   version "2.0.49"
@@ -1369,6 +1411,15 @@ babel-plugin-jest-hoist@^22.2.0:
   resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.2.0.tgz#bd34f39d652406669713b8c89e23ef25c890b993"
   integrity sha512-NwicD5n1YQaj6sM3PVULdPBDk1XdlWvh8xBeUJg3nqZwp79Vofb8Q7GOVeWoZZ/RMlMuJMMrEAgSQl/p392nLA==
 
+"babel-plugin-styled-components@>= 1":
+  version "1.9.2"
+  resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.9.2.tgz#0e6a6587454dcb1c9a362a8fd31fc0b075ccd260"
+  integrity sha512-McnheW8RkBkur/mQw7rEwQO/oUUruQ/nIIj5LIRpsVL8pzG1oo1Y53xyvAYeOfamIrl4/ta7g1G/kuTR1ekO3A==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.0.0"
+    babel-plugin-syntax-jsx "^6.18.0"
+    lodash "^4.17.10"
+
 babel-plugin-syntax-async-functions@^6.8.0:
   version "6.13.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
@@ -1404,7 +1455,7 @@ babel-plugin-syntax-flow@^6.18.0:
   resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d"
   integrity sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=
 
-babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0:
+babel-plugin-syntax-jsx@^6.18.0, babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0:
   version "6.18.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
   integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
@@ -3525,6 +3576,15 @@ css-to-react-native@^2.0.3:
     fbjs "^0.8.5"
     postcss-value-parser "^3.3.0"
 
+css-to-react-native@^2.2.2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.2.2.tgz#c077d0f7bf3e6c915a539e7325821c9dd01f9965"
+  integrity sha512-w99Fzop1FO8XKm0VpbQp3y5mnTnaS+rtCvS+ylSEOK76YXO5zoHQx/QMB1N54Cp+Ya9jB9922EHrh14ld4xmmw==
+  dependencies:
+    css-color-keywords "^1.0.0"
+    fbjs "^0.8.5"
+    postcss-value-parser "^3.3.0"
+
 css-what@2.1:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
@@ -8420,6 +8480,11 @@ mem@^1.1.0:
   dependencies:
     mimic-fn "^1.0.0"
 
+memoize-one@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.0.3.tgz#cdfdd942853f1a1b4c71c5336b8c49da0bf0273c"
+  integrity sha512-QmpUu4KqDmX0plH4u+tf0riMc1KHE1+lw95cMrLlXQAFOx/xnBtwhZ52XJxd9X2O6kwKBqX32kmhbhlobD0cuw==
+
 memory-fs@^0.4.0, memory-fs@~0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@@ -10667,6 +10732,19 @@ pubsweet-client@^4.0.4:
     styled-components "^3.2.5"
     styled-normalize "^3.0.1"
 
+pubsweet-component-login@^1.2.0, pubsweet-component-login@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/pubsweet-component-login/-/pubsweet-component-login-1.2.2.tgz#c5f0c0f4355a3f157e000d46852e2bcb3395ab1d"
+  integrity sha512-vQk7VGNI5SEQQmLTLGph9PWT7Am/lrWf7gVs1K0m9dcsWaKYHZ1AeLocMTTMwTa6OAfaWF9Mf8cewZW17dFaIw==
+  dependencies:
+    "@pubsweet/ui" "^9.0.1"
+    formik "1.3.0"
+    prop-types "^15.5.10"
+    react-redux "^5.0.6"
+    react-router-dom "^4.2.2"
+    react-router-redux "^5.0.0-alpha.9"
+    recompose "^0.26.0"
+
 pubsweet-server@10.0.1, pubsweet-server@^10.0.1:
   version "10.0.1"
   resolved "https://registry.yarnpkg.com/pubsweet-server/-/pubsweet-server-10.0.1.tgz#ac2ccc0ae1f42f2c97b044be3f90782b11b1d0fa"
@@ -11054,6 +11132,11 @@ react-is@^16.3.1:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22"
   integrity sha512-ybEM7YOr4yBgFd6w8dJqwxegqZGJNBZl6U27HnGKuTZmDvVrD5quWOK/wAnMywiZzW+Qsk+l4X2c70+thp/A8Q==
 
+react-is@^16.6.0:
+  version "16.6.3"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0"
+  integrity sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA==
+
 react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
@@ -12904,6 +12987,22 @@ styled-components@^3.4.2:
     stylis-rule-sheet "^0.0.10"
     supports-color "^3.2.3"
 
+styled-components@^4.1.1:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-4.1.2.tgz#f8a685e3b2bcd03c5beac7f2c02bb6ad237da9b3"
+  integrity sha512-NdvWatJ2WLqZxAvto+oH0k7GAC/TlAUJTrHoXJddjbCrU6U23EmVbb9LXJBF+d6q6hH+g9nQYOWYPUeX/Vlc2w==
+  dependencies:
+    "@emotion/is-prop-valid" "^0.6.8"
+    "@emotion/unitless" "^0.7.0"
+    babel-plugin-styled-components ">= 1"
+    css-to-react-native "^2.2.2"
+    memoize-one "^4.0.0"
+    prop-types "^15.5.4"
+    react-is "^16.6.0"
+    stylis "^3.5.0"
+    stylis-rule-sheet "^0.0.10"
+    supports-color "^5.5.0"
+
 styled-normalize@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/styled-normalize/-/styled-normalize-3.0.1.tgz#217efb96598690addd04699ca71af0db3473fea2"
@@ -13076,7 +13175,7 @@ supports-color@^5.2.0:
   dependencies:
     has-flag "^3.0.0"
 
-supports-color@^5.3.0:
+supports-color@^5.3.0, supports-color@^5.5.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
   integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==