diff --git a/packages/component-wizard/src/components/index.js b/packages/component-wizard/src/components/index.js
index 0c688b4063ed20527e1eb26cf90fbf1f54adddc8..276990a4799cd9b46ff99e66ecfe34e15ca1388d 100644
--- a/packages/component-wizard/src/components/index.js
+++ b/packages/component-wizard/src/components/index.js
@@ -1,4 +1,3 @@
-export { default as Files } from './Files'
 export { default as Wizard } from './Wizard'
 export { default as Progress } from './Progress'
 export { default as WizardPage } from './WizardPage'
diff --git a/packages/component-wizard/src/index.js b/packages/component-wizard/src/index.js
index b8ae61ac65482a61395c249a822ebc02d757d7e1..b43332ee55fa1efd10fa25e23eb759af7ccb18ea 100644
--- a/packages/component-wizard/src/index.js
+++ b/packages/component-wizard/src/index.js
@@ -4,7 +4,6 @@ module.exports = {
     reducers: {
       wizardConversion: () => require('./redux/conversion').default,
       autosave: () => require('./redux/autosave').default,
-      files: () => require('./redux/files').default,
     },
   },
 }
diff --git a/packages/component-wizard/src/redux/index.js b/packages/component-wizard/src/redux/index.js
index ac24b732b1d86380230cca1ac035c37ec58d9769..f767d735102b76804d7a5ba91ec0d08707d68bae 100644
--- a/packages/component-wizard/src/redux/index.js
+++ b/packages/component-wizard/src/redux/index.js
@@ -1,3 +1,2 @@
-export { default as files } from './files'
 export { default as autosave } from './autosave'
 export { default as conversion } from './conversion'
diff --git a/packages/components-faraday/src/components/AuthorList/AuthorAdder.js b/packages/components-faraday/src/components/AuthorList/AuthorAdder.js
index 5e9587c4091a115ba4184efcf983ec6648317597..a3380004f8142fa5c4498b6a82ee3c1468075db2 100644
--- a/packages/components-faraday/src/components/AuthorList/AuthorAdder.js
+++ b/packages/components-faraday/src/components/AuthorList/AuthorAdder.js
@@ -7,7 +7,9 @@ import { reduxForm } from 'redux-form'
 import { compose, withProps } from 'recompose'
 import { selectCurrentUser } from 'xpub-selectors'
 
+import { Spinner } from '../UIComponents/'
 import classes from './AuthorList.local.scss'
+import { getAuthorFetching } from '../../redux/authors'
 import { MenuItem, ValidatedTextField } from './FormItems'
 
 const countries = [
@@ -22,7 +24,13 @@ const emailRegex = new RegExp(/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/)
 const emailValidator = value =>
   emailRegex.test(value) ? undefined : 'Invalid email'
 
-const AuthorAdder = ({ authors, editMode, setEditMode, handleSubmit }) => (
+const AuthorAdder = ({
+  authors,
+  editMode,
+  setEditMode,
+  handleSubmit,
+  isFetching,
+}) => (
   <div className={classnames(classes.adder)}>
     <Button onClick={setEditMode(true)} primary>
       {authors.length === 0 ? '+ Add submitting author' : '+ Add author'}
@@ -54,9 +62,13 @@ const AuthorAdder = ({ authors, editMode, setEditMode, handleSubmit }) => (
         </div>
         <div className={classnames(classes['form-buttons'])}>
           <Button onClick={setEditMode(false)}>Cancel</Button>
-          <Button onClick={handleSubmit} primary>
-            Save
-          </Button>
+          {!isFetching ? (
+            <Button onClick={handleSubmit} primary>
+              Save
+            </Button>
+          ) : (
+            <Spinner />
+          )}
         </div>
       </div>
     )}
@@ -66,6 +78,7 @@ const AuthorAdder = ({ authors, editMode, setEditMode, handleSubmit }) => (
 export default compose(
   connect(state => ({
     currentUser: selectCurrentUser(state),
+    isFetching: getAuthorFetching(state),
   })),
   withProps(({ currentUser: { admin, username, email }, authors }) => {
     if (!admin && authors.length === 0) {
@@ -83,7 +96,7 @@ export default compose(
     onSubmit: (
       values,
       dispatch,
-      { authors, addAuthor, setEditMode, reset, match },
+      { authors, addAuthor, setEditMode, setFormAuthors, reset, match },
     ) => {
       const collectionId = get(match, 'params.project')
       const fragmentId = get(match, 'params.version')
@@ -96,7 +109,9 @@ export default compose(
         },
         collectionId,
         fragmentId,
-      ).then(() => {
+      ).then(author => {
+        const newAuthors = [...authors, author]
+        setFormAuthors(newAuthors)
         reset()
         setEditMode(false)()
       })
diff --git a/packages/components-faraday/src/components/AuthorList/AuthorEditor.js b/packages/components-faraday/src/components/AuthorList/AuthorEditor.js
index 6ceffb8430c9b9f93459763d1a68b04a551a19d4..10f4f5a81c2915fc8ff19986f2b87a21f1d3e88b 100644
--- a/packages/components-faraday/src/components/AuthorList/AuthorEditor.js
+++ b/packages/components-faraday/src/components/AuthorList/AuthorEditor.js
@@ -1,14 +1,13 @@
 import React from 'react'
-import PropTypes from 'prop-types'
 import classnames from 'classnames'
-import { connect } from 'react-redux'
+import { compose } from 'recompose'
 import { Button } from '@pubsweet/ui'
+import { connect } from 'react-redux'
 import { reduxForm } from 'redux-form'
-import { withRouter } from 'react-router-dom'
-import { compose, getContext } from 'recompose'
 
+import { Spinner } from '../UIComponents'
+import { getAuthorFetching } from '../../redux/authors'
 import { ValidatedTextField, MenuItem } from './FormItems'
-import { getFragmentAuthors, setAuthors } from '../../redux/authors'
 
 import classes from './AuthorList.local.scss'
 
@@ -24,7 +23,7 @@ const emailRegex = new RegExp(/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/)
 const emailValidator = value =>
   emailRegex.test(value) ? undefined : 'Invalid email'
 
-const AuthorEdit = ({ setAuthorEdit, handleSubmit }) => (
+const AuthorEdit = ({ isFetching, setAuthorEdit, handleSubmit }) => (
   <div className={classnames(classes['editor-body'])}>
     <div className={classnames(classes.row)}>
       <ValidatedTextField isRequired label="First name" name="edit.firstName" />
@@ -49,30 +48,27 @@ const AuthorEdit = ({ setAuthorEdit, handleSubmit }) => (
 
     <div className={classnames(classes['form-buttons'])}>
       <Button onClick={setAuthorEdit(-1)}>Cancel</Button>
-      <Button onClick={handleSubmit} primary>
-        Save
-      </Button>
+      {!isFetching ? (
+        <Button onClick={handleSubmit} primary>
+          Save
+        </Button>
+      ) : (
+        <Spinner />
+      )}
     </div>
   </div>
 )
 
 export default compose(
-  withRouter,
-  getContext({ version: PropTypes.object, project: PropTypes.object }),
-  connect(
-    (state, { match: { params: { version } } }) => ({
-      authors: getFragmentAuthors(state, version),
-    }),
-    {
-      setAuthors,
-    },
-  ),
+  connect(state => ({
+    isFetching: getAuthorFetching(state),
+  })),
   reduxForm({
     form: 'edit',
     onSubmit: (
       values,
       dispatch,
-      { setAuthorEdit, setAuthors, project, version, authors, index, ...rest },
+      { setAuthorEdit, setAuthors, authors, index, changeForm },
     ) => {
       const newAuthors = [
         ...authors.slice(0, index),
@@ -80,7 +76,7 @@ export default compose(
         ...authors.slice(index + 1),
       ]
       setAuthorEdit(-1)()
-      setAuthors(newAuthors, version.id)
+      setAuthors(newAuthors)
     },
   }),
 )(AuthorEdit)
diff --git a/packages/components-faraday/src/components/AuthorList/AuthorList.js b/packages/components-faraday/src/components/AuthorList/AuthorList.js
index 9576a82d93eea2f15673629b88232beafe349129..06f4f7bb27e3fb4350c902059019f360770690db 100644
--- a/packages/components-faraday/src/components/AuthorList/AuthorList.js
+++ b/packages/components-faraday/src/components/AuthorList/AuthorList.js
@@ -9,15 +9,10 @@ import {
   lifecycle,
   withState,
 } from 'recompose'
-import { change } from 'redux-form'
+import { change as changeForm } from 'redux-form'
 import { SortableList } from 'pubsweet-components-faraday/src/components'
 
-import {
-  addAuthor,
-  getFragmentAuthors,
-  setAuthors,
-  moveAuthors,
-} from '../../redux/authors'
+import { addAuthor } from '../../redux/authors'
 
 import Author from './Author'
 import StaticList from './StaticList'
@@ -43,6 +38,7 @@ const Authors = ({
   editMode,
   setEditMode,
   editedAuthor,
+  setFormAuthors,
   ...rest
 }) => (
   <div>
@@ -53,12 +49,14 @@ const Authors = ({
       editMode={editMode}
       match={match}
       setEditMode={setEditMode}
+      setFormAuthors={setFormAuthors}
     />
     {editedAuthor > -1 ? (
       <StaticList
         authors={authors}
         editComponent={AuthorEditor}
         editIndex={editedAuthor}
+        setFormAuthors={setFormAuthors}
         {...rest}
       />
     ) : (
@@ -79,38 +77,38 @@ const Authors = ({
 export default compose(
   withRouter,
   getContext({ version: PropTypes.object, project: PropTypes.object }),
-  connect(
-    (state, { match: { params: { version } } }) => ({
-      authors: getFragmentAuthors(state, version),
-    }),
-    {
-      addAuthor,
-      setAuthors,
-      moveAuthors,
-      formChange: change,
-    },
-  ),
+  connect(null, {
+    addAuthor,
+    changeForm,
+  }),
+  withState('authors', 'setAuthors', []),
   lifecycle({
     componentDidMount() {
       const { version, setAuthors } = this.props
-      setAuthors(version.authors, version.id)
+      setAuthors(version.authors)
     },
   }),
   withState('editMode', 'setEditMode', false),
   withState('editedAuthor', 'setEditedAuthor', -1),
   withHandlers({
-    setAuthorEdit: ({ setEditedAuthor, formChange }) => editedAuthor => e => {
+    setFormAuthors: ({ setAuthors, changeForm }) => authors => {
+      setAuthors(authors)
+      changeForm('wizard', 'authors', authors)
+    },
+  }),
+  withHandlers({
+    setAuthorEdit: ({ setEditedAuthor, changeForm }) => editedAuthor => e => {
       e && e.preventDefault && e.preventDefault()
-      formChange('wizard', 'editMode', editedAuthor > -1)
+      changeForm('wizard', 'editMode', editedAuthor > -1)
       setEditedAuthor(prev => editedAuthor)
     },
-    setEditMode: ({ setEditMode, formChange }) => mode => e => {
+    setEditMode: ({ setEditMode, changeForm }) => mode => e => {
       e && e.preventDefault()
-      formChange('wizard', 'editMode', mode)
+      changeForm('wizard', 'editMode', mode)
       setEditMode(v => mode)
     },
-    dropItem: ({ authors, project, version, setAuthors }) => () => {
-      setAuthors(authors, version.id)
+    dropItem: ({ authors, setFormAuthors }) => () => {
+      setFormAuthors(authors)
     },
     countryParser: () => countryCode =>
       countries.find(c => c.value === countryCode).label,
@@ -119,36 +117,23 @@ export default compose(
       if (isCorresponding) return `#${index + 1} Corresponding author`
       return `#${index + 1} Author`
     },
-    moveAuthor: ({
-      authors,
-      moveAuthors,
-      project,
-      version,
-      match: { params },
-    }) => (dragIndex, hoverIndex) => {
+    moveAuthor: ({ authors, setAuthors, changeForm }) => (
+      dragIndex,
+      hoverIndex,
+    ) => {
       const newAuthors = SortableList.moveItem(authors, dragIndex, hoverIndex)
-      moveAuthors(newAuthors, params.version)
+      setAuthors(newAuthors)
     },
-    removeAuthor: ({
-      authors,
-      project,
-      version,
-      setAuthors,
-    }) => authorEmail => () => {
+    removeAuthor: ({ authors, setFormAuthors }) => authorEmail => () => {
       const newAuthors = authors.filter(a => a.email !== authorEmail)
-      setAuthors(newAuthors, version.id)
+      setFormAuthors(newAuthors)
     },
-    setAsCorresponding: ({
-      authors,
-      setAuthors,
-      version,
-      project,
-    }) => authorEmail => () => {
+    setAsCorresponding: ({ authors, setFormAuthors }) => authorEmail => () => {
       const newAuthors = authors.map(a => ({
         ...a,
         isCorresponding: a.isSubmitting || a.email === authorEmail,
       }))
-      setAuthors(newAuthors, version.id)
+      setFormAuthors(newAuthors)
     },
   }),
 )(Authors)
diff --git a/packages/components-faraday/src/components/AuthorList/StaticList.js b/packages/components-faraday/src/components/AuthorList/StaticList.js
index 5cb6432fb475ead78532bd019ae4f80a8b09b8fd..7fe49d1efc1aaf9be939954d8a86ea62b1027430 100644
--- a/packages/components-faraday/src/components/AuthorList/StaticList.js
+++ b/packages/components-faraday/src/components/AuthorList/StaticList.js
@@ -5,6 +5,7 @@ import Author from './Author'
 export default ({
   authors,
   editIndex,
+  setFormAuthors,
   removeAuthor,
   countryParser,
   editComponent,
@@ -14,22 +15,24 @@ export default ({
 }) => (
   <div>
     {authors.map(
-      (a, index) =>
+      (author, index) =>
         index === editIndex ? (
           React.createElement(editComponent, {
             key: 'author-editor',
+            authors,
             index,
             initialValues: {
-              edit: a,
+              edit: author,
             },
+            setAuthors: setFormAuthors,
             setAuthorEdit,
             countryParser,
             parseAuthorType,
           })
         ) : (
           <Author
-            key={a.firstName}
-            {...a}
+            key={author.firstName}
+            {...author}
             countryParser={countryParser}
             index={index}
             parseAuthorType={parseAuthorType}
diff --git a/packages/component-wizard/src/components/FileDropzone.js b/packages/components-faraday/src/components/Files/FileDropzone.js
similarity index 100%
rename from packages/component-wizard/src/components/FileDropzone.js
rename to packages/components-faraday/src/components/Files/FileDropzone.js
diff --git a/packages/component-wizard/src/components/FileDropzone.local.scss b/packages/components-faraday/src/components/Files/FileDropzone.local.scss
similarity index 100%
rename from packages/component-wizard/src/components/FileDropzone.local.scss
rename to packages/components-faraday/src/components/Files/FileDropzone.local.scss
diff --git a/packages/component-wizard/src/components/FileItem.js b/packages/components-faraday/src/components/Files/FileItem.js
similarity index 100%
rename from packages/component-wizard/src/components/FileItem.js
rename to packages/components-faraday/src/components/Files/FileItem.js
diff --git a/packages/component-wizard/src/components/FileItem.local.scss b/packages/components-faraday/src/components/Files/FileItem.local.scss
similarity index 100%
rename from packages/component-wizard/src/components/FileItem.local.scss
rename to packages/components-faraday/src/components/Files/FileItem.local.scss
diff --git a/packages/component-wizard/src/components/FilePicker.js b/packages/components-faraday/src/components/Files/FilePicker.js
similarity index 100%
rename from packages/component-wizard/src/components/FilePicker.js
rename to packages/components-faraday/src/components/Files/FilePicker.js
diff --git a/packages/component-wizard/src/components/FileSection.js b/packages/components-faraday/src/components/Files/FileSection.js
similarity index 95%
rename from packages/component-wizard/src/components/FileSection.js
rename to packages/components-faraday/src/components/Files/FileSection.js
index 5f3726de3d4bea1d9aba109b9feeafba4c9e4b73..f495196cc460ea3493e08b689ef16626aa30973a 100644
--- a/packages/component-wizard/src/components/FileSection.js
+++ b/packages/components-faraday/src/components/Files/FileSection.js
@@ -5,7 +5,10 @@ import { Icon } from '@pubsweet/ui'
 import { DropTarget } from 'react-dnd'
 import { NativeTypes } from 'react-dnd-html5-backend'
 import { compose, getContext, withHandlers, withState } from 'recompose'
-import { SortableList } from 'pubsweet-components-faraday/src/components'
+import {
+  SortableList,
+  Spinner,
+} from 'pubsweet-components-faraday/src/components'
 
 import FileItem from './FileItem'
 import FilePicker from './FilePicker'
@@ -73,9 +76,7 @@ const FileSection = ({
                 </div>
               </FilePicker>
             ) : (
-              <div className={classnames(classes.rotate, classes.icon)}>
-                <Icon size={16}>loader</Icon>
-              </div>
+              <Spinner />
             )}
           </div>
           <span className={classnames(classes.error)}>{error}</span>
diff --git a/packages/component-wizard/src/components/FileSection.local.scss b/packages/components-faraday/src/components/Files/FileSection.local.scss
similarity index 50%
rename from packages/component-wizard/src/components/FileSection.local.scss
rename to packages/components-faraday/src/components/Files/FileSection.local.scss
index 6f1429c741c0d8b4a020a7bd0e0f48efc30594e6..709f425a064251d4bf60391f3517e96d2e4bb7a9 100644
--- a/packages/component-wizard/src/components/FileSection.local.scss
+++ b/packages/components-faraday/src/components/Files/FileSection.local.scss
@@ -60,48 +60,3 @@
 .is-over {
   background-color: #ddd;
 }
-
-@keyframes rotating {
-  from {
-    -o-transform: rotate(0deg);
-    -ms-transform: rotate(0deg);
-    -moz-transform: rotate(0deg);
-    -webkit-transform: rotate(0deg);
-    transform: rotate(0deg);
-  }
-
-  to {
-    -o-transform: rotate(360deg);
-    -ms-transform: rotate(360deg);
-    -moz-transform: rotate(360deg);
-    -webkit-transform: rotate(360deg);
-    transform: rotate(360deg);
-  }
-}
-
-@-webkit-keyframes rotating {
-  from {
-    -webkit-transform: rotate(0deg);
-    transform: rotate(0deg);
-  }
-
-  to {
-    -webkit-transform: rotate(360deg);
-    transform: rotate(360deg);
-  }
-}
-
-.rotate {
-  -webkit-animation: rotating 1.5s linear infinite;
-  -moz-animation: rotating 1.5s linear infinite;
-  -ms-animation: rotating 1.5s linear infinite;
-  -o-animation: rotating 1.5s linear infinite;
-  animation: rotating 1.5s linear infinite;
-}
-
-.icon {
-  align-items: center;
-  display: flex;
-  justify-content: center;
-  margin: 0 5px 0 0;
-}
diff --git a/packages/component-wizard/src/components/Files.js b/packages/components-faraday/src/components/Files/Files.js
similarity index 99%
rename from packages/component-wizard/src/components/Files.js
rename to packages/components-faraday/src/components/Files/Files.js
index b4e3683ae3fda8054b55f618f65b7903831bae1d..f61eaca9b74c0e6dc61161f5300acf1a80d817d8 100644
--- a/packages/component-wizard/src/components/Files.js
+++ b/packages/components-faraday/src/components/Files/Files.js
@@ -21,7 +21,7 @@ import {
   deleteFile,
   getRequestStatus,
   getSignedUrl,
-} from '../redux/files'
+} from '../../redux/files'
 
 const Files = ({
   files,
diff --git a/packages/component-wizard/src/components/Files.local.scss b/packages/components-faraday/src/components/Files/Files.local.scss
similarity index 100%
rename from packages/component-wizard/src/components/Files.local.scss
rename to packages/components-faraday/src/components/Files/Files.local.scss
diff --git a/packages/components-faraday/src/components/Files/index.js b/packages/components-faraday/src/components/Files/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..6df727395c45e4df9e5db1d031a4d43146ad8440
--- /dev/null
+++ b/packages/components-faraday/src/components/Files/index.js
@@ -0,0 +1 @@
+export { default as Files } from './Files'
diff --git a/packages/components-faraday/src/components/SortableList/SortableList.js b/packages/components-faraday/src/components/SortableList/SortableList.js
index b19cc9d884818c75682cf6637b6c499a4f2fc9f4..ccbe292df8aa82e92a813650483ba8fb5b90c1d2 100644
--- a/packages/components-faraday/src/components/SortableList/SortableList.js
+++ b/packages/components-faraday/src/components/SortableList/SortableList.js
@@ -93,6 +93,7 @@ const DecoratedItem = compose(
 
 const SortableList = ({
   items,
+  itemKey = 'id',
   moveItem,
   listItem,
   dragHandle,
@@ -104,7 +105,7 @@ const SortableList = ({
       <DecoratedItem
         dragHandle={dragHandle}
         index={i}
-        key={item.name || Math.random()}
+        key={item[itemKey]}
         listItem={listItem}
         moveItem={moveItem}
         {...item}
diff --git a/packages/components-faraday/src/components/SortableList/SortableList.md b/packages/components-faraday/src/components/SortableList/SortableList.md
index e6da217d33f181dc1fca556935a0f214ff3aeb2d..3eb03059c3b668e86c210ea3c31221abb05349ec 100644
--- a/packages/components-faraday/src/components/SortableList/SortableList.md
+++ b/packages/components-faraday/src/components/SortableList/SortableList.md
@@ -2,13 +2,15 @@ A sortable list implemented with `react-dnd`.
 
 ## Props
 
-|    Prop    |                                                                 Description                                                                  | Required |        Default        |
-| :--------: | :------------------------------------------------------------------------------------------------------------------------------------------: | :------: | :-------------------: |
-|   items    |                                                       The items of the sortable list.                                                        |   true   |          []           |
-|  listItem  | A React component that will be rendered for each item of the list. Receives `isDragging`, `isOver` and all other props from the items array. |   true   |         none          |
-|  moveItem  |       Function to be called when moving an item through the list. SortableList will provide the dragIndex of hoverIndex of the items.        |   true   | SortableList.moveItem |
-| dragHandle |                            A React component for the drag handle. If not present, the whole item can be dragged.                             |  false   |         none          |
-|  dropItem  |                            Function to be called when dropping an item. The index of the dragged item is passed.                             |  false   |         none          |
+|    Prop    |                                                                 Description                                                                  | Required |        Default        | Type |
+| :--------: | :------------------------------------------------------------------------------------------------------------------------------------------: | :------: | :-------------------: | :---: |
+|   items    |                                                       The items of the sortable list.                                                        |   true   |          []           | Array |
+| itemKey | Value used for key when mapping over items. | true | 'id' | string |
+|  listItem  | A React component that will be rendered for each item of the list. Receives `isDragging`, `isOver` and all other props from the items array. |   true   |         none          | React component |
+|  moveItem  |       Function to be called when moving an item through the list. SortableList will provide the dragIndex of hoverIndex of the items.        |   true   | none | function  |
+| dragHandle |                            A React component for the drag handle. If not present, the whole item can be dragged.                             |  false   |         none          | React component |
+|  dropItem  |                            Function to be called when dropping an item. The index of the dragged item is passed.                             |  false   |         none          | function |
+| beginDragProps | Array of keys to pick from the dragged object when beginning drag.  | false | [] | Array(string) |
 
 ## Usage
 
diff --git a/packages/components-faraday/src/components/UIComponents/Spinner.js b/packages/components-faraday/src/components/UIComponents/Spinner.js
new file mode 100644
index 0000000000000000000000000000000000000000..42c828c4bd85664bc3885daf6c0b0b70eec33a0e
--- /dev/null
+++ b/packages/components-faraday/src/components/UIComponents/Spinner.js
@@ -0,0 +1,13 @@
+import React from 'react'
+import classnames from 'classnames'
+import { Icon } from '@pubsweet/ui'
+
+import classes from './Spinner.local.scss'
+
+const Spinner = () => (
+  <div className={classnames(classes.rotate, classes.icon)}>
+    <Icon size={16}>loader</Icon>
+  </div>
+)
+
+export default Spinner
diff --git a/packages/components-faraday/src/components/UIComponents/Spinner.local.scss b/packages/components-faraday/src/components/UIComponents/Spinner.local.scss
new file mode 100644
index 0000000000000000000000000000000000000000..b82f01d0e4f0d7113d1e63f67845f40120027477
--- /dev/null
+++ b/packages/components-faraday/src/components/UIComponents/Spinner.local.scss
@@ -0,0 +1,44 @@
+@keyframes rotating {
+  from {
+    -o-transform: rotate(0deg);
+    -ms-transform: rotate(0deg);
+    -moz-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  to {
+    -o-transform: rotate(360deg);
+    -ms-transform: rotate(360deg);
+    -moz-transform: rotate(360deg);
+    -webkit-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
+}
+
+@-webkit-keyframes rotating {
+  from {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  to {
+    -webkit-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
+}
+
+.rotate {
+  -webkit-animation: rotating 1.8s linear infinite;
+  -moz-animation: rotating 1.8s linear infinite;
+  -ms-animation: rotating 1.8s linear infinite;
+  -o-animation: rotating 1.8s linear infinite;
+  animation: rotating 1.8s linear infinite;
+}
+
+.icon {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  margin: 0 5px 0 0;
+}
diff --git a/packages/components-faraday/src/components/UIComponents/index.js b/packages/components-faraday/src/components/UIComponents/index.js
index 8c2c826a28aa0777ba243088dec43647bbc965e4..0f79f62b6f773549c5da460c041232e47820dfb3 100644
--- a/packages/components-faraday/src/components/UIComponents/index.js
+++ b/packages/components-faraday/src/components/UIComponents/index.js
@@ -1,2 +1,3 @@
 export { default as Logo } from './Logo'
+export { default as Spinner } from './Spinner'
 export { default as Dropdown } from './Dropdown'
diff --git a/packages/components-faraday/src/components/index.js b/packages/components-faraday/src/components/index.js
index da51b2a179ed3cefad04ed44d0c2dc808d45a476..210934a75eb51d1f7c9f9b38e344ab3af2852003 100644
--- a/packages/components-faraday/src/components/index.js
+++ b/packages/components-faraday/src/components/index.js
@@ -1,5 +1,6 @@
-export { default as SortableList } from './SortableList/SortableList'
+export { default as Files } from './Files/Files'
 export { default as AuthorList } from './AuthorList/AuthorList'
+export { default as SortableList } from './SortableList/SortableList'
 
-export { Dropdown, Logo } from './UIComponents'
 export { DragHandle } from './AuthorList/FormItems'
+export { Dropdown, Logo, Spinner } from './UIComponents'
diff --git a/packages/components-faraday/src/index.js b/packages/components-faraday/src/index.js
index 70c96833395b805503aba42b54417baee276e117..3c0ed73cabca75cdc957af41d97563815a4e1498 100644
--- a/packages/components-faraday/src/index.js
+++ b/packages/components-faraday/src/index.js
@@ -3,6 +3,7 @@ module.exports = {
     components: [() => require('./components')],
     reducers: {
       authors: () => require('./redux/authors').default,
+      files: () => require('./redux/files').default,
       filters: () => require('./components/Dashboard/redux/filters').default,
     },
   },
diff --git a/packages/components-faraday/src/redux/authors.js b/packages/components-faraday/src/redux/authors.js
index dc4d4ad5255f7e31cbc1a3aef6833670d4c6b76c..8a9e3a9c7f480b1badf25146c66e9de7f88d84ce 100644
--- a/packages/components-faraday/src/redux/authors.js
+++ b/packages/components-faraday/src/redux/authors.js
@@ -1,49 +1,64 @@
 import { get } from 'lodash'
-import { actions } from 'pubsweet-client'
 import * as api from 'pubsweet-client/src/helpers/api'
-import { change } from 'redux-form'
 
 // constants
-export const SET_AUTHORS = 'authors/SET_AUTHORS'
+const REQUEST = 'authors/REQUEST'
+const FAILURE = 'authors/FAILURE'
+const SUCCESS = 'authors/SUCCESS'
 
-const _setAuthors = (authors, fragmentId) => ({
-  type: SET_AUTHORS,
-  authors,
-  fragmentId,
+// actions
+export const authorRequest = () => ({
+  type: REQUEST,
 })
 
-// actions
-export const setAuthors = (authors, fragmentId) => dispatch => {
-  dispatch(change('wizard', 'authors', authors))
-  dispatch(_setAuthors(authors, fragmentId))
-}
+export const authorFaiure = error => ({
+  type: FAILURE,
+  error,
+})
 
-export const moveAuthors = (authors, fragmentId) => dispatch => {
-  dispatch(_setAuthors(authors, fragmentId))
-}
+export const authorSuccess = () => ({
+  type: SUCCESS,
+})
 
-export const addAuthor = (author, collectionId, fragmentId) => dispatch =>
-  api
+export const addAuthor = (author, collectionId, fragmentId) => dispatch => {
+  dispatch(authorRequest())
+  return api
     .create(
       `/collections/${collectionId}/fragments/${fragmentId}/authors`,
       author,
     )
-    .then(() =>
-      dispatch(actions.getFragment({ id: collectionId }, { id: fragmentId })),
-    )
-    .then(({ fragment: { authors, id } }) => dispatch(setAuthors(authors, id)))
+    .then(author => {
+      dispatch(authorSuccess())
+      return author
+    })
+    .catch(err => dispatch(authorFaiure(err)))
+}
 
 // selectors
 export const getFragmentAuthors = (state, fragmentId) =>
   get(state, `authors.${fragmentId}`) || []
 
-export default (state = {}, action) => {
+export const getAuthorFetching = state => state.authors.isFetching
+export const getAuthorError = state => state.authors.error
+
+const initialState = { isFetching: false, error: null }
+
+export default (state = initialState, action) => {
   switch (action.type) {
-    case SET_AUTHORS:
+    case 'UPDATE_FRAGMENT_REQUEST':
+    case REQUEST:
+      return {
+        ...initialState,
+        isFetching: true,
+      }
+    case FAILURE:
       return {
-        ...state,
-        [action.fragmentId]: action.authors,
+        ...initialState,
+        error: action.error,
       }
+    case 'UPDATE_FRAGMENT_SUCCESS':
+    case SUCCESS:
+      return initialState
     default:
       return state
   }
diff --git a/packages/component-wizard/src/redux/files.js b/packages/components-faraday/src/redux/files.js
similarity index 100%
rename from packages/component-wizard/src/redux/files.js
rename to packages/components-faraday/src/redux/files.js
diff --git a/packages/xpub-aws/config/upload-validations-test.js b/packages/xpub-aws/config/upload-validations-test.js
new file mode 100644
index 0000000000000000000000000000000000000000..23fa37d7d5a7b0403731429eb910f5f402799000
--- /dev/null
+++ b/packages/xpub-aws/config/upload-validations-test.js
@@ -0,0 +1,19 @@
+const Joi = require('joi')
+
+module.exports = {
+  manuscripts: Joi.any()
+    .valid([
+      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+      'application/pdf',
+      'application/msword',
+    ])
+    .error(new Error('Only Word documents and PDFs are allowed')),
+  supplementary: Joi.any(),
+  coverLetter: Joi.any()
+    .valid([
+      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+      'application/pdf',
+      'application/msword',
+    ])
+    .error(new Error('Only Word documents and PDFs are allowed')),
+}
diff --git a/packages/xpub-aws/src/middeware/upload.js b/packages/xpub-aws/src/middeware/upload.js
index e15c07ff36589eecaf6ded895da7e2a417fe2fa2..73d9a14367f4708d162d916f0fcfeba84fa14fda 100644
--- a/packages/xpub-aws/src/middeware/upload.js
+++ b/packages/xpub-aws/src/middeware/upload.js
@@ -1,6 +1,12 @@
 const multer = require('multer')
 const multerS3 = require('multer-s3')
 const uuid = require('uuid')
+const Joi = require('joi')
+const _ = require('lodash')
+const config = require('config')
+
+const s3Config = _.get(config, 'pubsweet-component-aws-s3')
+const uploadValidations = require(s3Config.validations)
 
 const setupMulter = s3 => {
   const upload = multer({
@@ -22,21 +28,16 @@ const setupMulter = s3 => {
 }
 
 const validateFile = (req, file, cb) => {
-  if (
-    req.body.fileType === 'manuscripts' ||
-    req.body.fileType === 'coverLetter'
-  ) {
-    if (
-      file.mimetype === 'application/pdf' ||
-      file.mimetype === 'application/msword' ||
-      file.mimetype ===
-        'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
-    ) {
-      return cb(null, true)
-    }
-    req.fileValidationError = 'Only Word documents and PDFs are allowed'
+  const fileType = req.body.fileType
+  const mimetype = file.mimetype
+
+  const valid = Joi.validate({ [fileType]: mimetype }, uploadValidations)
+
+  if (valid.error) {
+    req.fileValidationError = valid.error.message
     return cb(null, false)
   }
+
   return cb(null, true)
 }
 
diff --git a/packages/xpub-faraday/app/config/journal/submit-wizard.js b/packages/xpub-faraday/app/config/journal/submit-wizard.js
index 261390aa2c296cd39170e2d5dadb2171331dc079..3a63214bbc415175b315d8bcefd0a597ce6d0717 100644
--- a/packages/xpub-faraday/app/config/journal/submit-wizard.js
+++ b/packages/xpub-faraday/app/config/journal/submit-wizard.js
@@ -3,8 +3,7 @@ import { AbstractEditor, TitleEditor } from 'xpub-edit'
 import { Menu, CheckboxGroup, YesOrNo, TextField } from '@pubsweet/ui'
 import uploadFileFn from 'xpub-upload'
 import { required, minChars, minSize } from 'xpub-validators'
-import { Files } from 'pubsweet-component-wizard/src/components/'
-import { AuthorList } from 'pubsweet-components-faraday/src/components'
+import { AuthorList, Files } from 'pubsweet-components-faraday/src/components'
 
 import { declarations } from './'
 import issueTypes from './issues-types'
diff --git a/packages/xpub-faraday/app/config/journal/wizard-validators.js b/packages/xpub-faraday/app/config/journal/wizard-validators.js
index 7816aee812bfed54a505ccb95dfb240c3a7a0c14..8504e61f02765ae8e83a9f2f43063801258e2d10 100644
--- a/packages/xpub-faraday/app/config/journal/wizard-validators.js
+++ b/packages/xpub-faraday/app/config/journal/wizard-validators.js
@@ -31,7 +31,8 @@ export const editModeEnabled = value => {
 }
 
 export const requiredFiles = (valus, formValues) => {
-  if (get(formValues, 'files.manuscripts').length === 0) {
+  const manuscripts = get(formValues, 'files.manuscripts')
+  if (manuscripts && manuscripts.length === 0) {
     return 'At least one main manuscript file is needed.'
   }
   return undefined
diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js
index ac142cf190c5a2949aeb6142ab589fbb04fa27e3..66110156bd835611193475733fbc26f0c23635d1 100644
--- a/packages/xpub-faraday/config/default.js
+++ b/packages/xpub-faraday/config/default.js
@@ -46,5 +46,17 @@ module.exports = {
       'editoria-typescript': '2',
     },
   },
-  publicKeys: ['pubsweet-client', 'authsome', 'validations'],
+  'pubsweet-component-aws-s3': {
+    secretAccessKey: process.env.AWS_SECRET_KEY,
+    accessKeyId: process.env.AWS_ACCESS_KEY,
+    region: process.env.AWS_REGION,
+    sender: process.env.PUBSWEET_EMAIL_SENDER || 'dev@mailinator.com',
+    validations: path.resolve(__dirname, 'upload-validations.js'),
+  },
+  publicKeys: [
+    'pubsweet-client',
+    'authsome',
+    'validations',
+    'pubsweet-component-aws-s3',
+  ],
 }
diff --git a/packages/xpub-faraday/config/upload-validations.js b/packages/xpub-faraday/config/upload-validations.js
new file mode 100644
index 0000000000000000000000000000000000000000..23fa37d7d5a7b0403731429eb910f5f402799000
--- /dev/null
+++ b/packages/xpub-faraday/config/upload-validations.js
@@ -0,0 +1,19 @@
+const Joi = require('joi')
+
+module.exports = {
+  manuscripts: Joi.any()
+    .valid([
+      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+      'application/pdf',
+      'application/msword',
+    ])
+    .error(new Error('Only Word documents and PDFs are allowed')),
+  supplementary: Joi.any(),
+  coverLetter: Joi.any()
+    .valid([
+      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+      'application/pdf',
+      'application/msword',
+    ])
+    .error(new Error('Only Word documents and PDFs are allowed')),
+}