diff --git a/packages/component-manuscript/src/components/Files.js b/packages/component-manuscript/src/components/Files.js
index e48eab69bd603a2d03a5daab1761ed6bbc83116f..100f0563445db1c8838b207f0c91cf81f1893fa0 100644
--- a/packages/component-manuscript/src/components/Files.js
+++ b/packages/component-manuscript/src/components/Files.js
@@ -1,46 +1,8 @@
 import React, { Fragment } from 'react'
-import { last } from 'lodash'
 import styled, { css } from 'styled-components'
-import { th, Icon } from '@pubsweet/ui'
+import { th } from '@pubsweet/ui'
 
-const parseFileSize = (size = 0) => {
-  const kbSize = Math.floor(size / 1000)
-  const mbSize = Math.floor(kbSize / 1000)
-  const gbSize = Math.floor(mbSize / 1000)
-
-  if (gbSize) {
-    return `${gbSize} GB`
-  } else if (mbSize) {
-    return `${mbSize} MB`
-  } else if (kbSize) {
-    return `${kbSize} kB`
-  }
-  return `${size} bytes`
-}
-
-const hasPreview = (name = '') => {
-  const extension = last(name.split('.'))
-  return ['pdf', 'png', 'jpg'].includes(extension)
-}
-
-const File = ({ name = 'filename', size, id, previewFile, downloadFile }) => (
-  <FileRoot>
-    {hasPreview(name) && (
-      <IconButton onClick={previewFile(id)}>
-        <Icon primary size={3}>
-          eye
-        </Icon>
-      </IconButton>
-    )}
-    <IconButton onClick={downloadFile(id, name)}>
-      <Icon primary size={3}>
-        download
-      </Icon>
-    </IconButton>
-    <FileName>{name}</FileName>
-    <FileSize>{parseFileSize(size)}</FileSize>
-  </FileRoot>
-)
+import { FileItem } from 'pubsweet-components-faraday/src/components/Files'
 
 const Files = ({
   previewFile,
@@ -55,7 +17,9 @@ const Files = ({
           <div />
         </Header>
         {manuscripts.map(file => (
-          <File
+          <FileItem
+            compact
+            id={file.id}
             key={file.id}
             {...file}
             downloadFile={downloadFile}
@@ -71,7 +35,9 @@ const Files = ({
           <div />
         </Header>
         {supplementary.map(file => (
-          <File
+          <FileItem
+            compact
+            id={file.id}
             key={file.id}
             {...file}
             downloadFile={downloadFile}
@@ -87,7 +53,9 @@ const Files = ({
           <div />
         </Header>
         {coverLetter.map(file => (
-          <File
+          <FileItem
+            compact
+            id={file.id}
             key={file.id}
             {...file}
             downloadFile={downloadFile}
@@ -108,34 +76,6 @@ const defaultText = css`
   font-size: ${th('fontSizeBaseSmall')};
 `
 
-const FileName = styled.span`
-  ${defaultText};
-  margin: 0 ${th('subGridUnit')};
-`
-const FileSize = FileName.extend`
-  margin-left: ${th('subGridUnit')};
-`
-
-const IconButton = styled.div`
-  align-items: center;
-  cursor: pointer;
-  display: flex;
-  justify-content: center;
-  margin: 0 ${th('subGridUnit')};
-  &:hover {
-    opacity: 0.7;
-  }
-`
-
-const FileRoot = styled.div`
-  align-items: center;
-  border: ${th('borderDefault')};
-  display: flex;
-  flex-direction: row;
-  margin-bottom: ${th('subGridUnit')};
-  padding: ${th('subGridUnit')};
-`
-
 const Header = styled.div`
   align-self: stretch;
   align-items: center;
diff --git a/packages/component-manuscript/src/components/ReviewerReportForm.js b/packages/component-manuscript/src/components/ReviewerReportForm.js
index 300fc7c0041480a9a442fc3f5ade4b3b4667137f..3f8fe374cb496a6363c9657bc576a1a51f2ec3ad 100644
--- a/packages/component-manuscript/src/components/ReviewerReportForm.js
+++ b/packages/component-manuscript/src/components/ReviewerReportForm.js
@@ -12,6 +12,16 @@ import {
 } from 'redux-form'
 import AutosaveIndicator from 'pubsweet-component-wizard/src/components/AutosaveIndicator'
 
+import {
+  uploadFile,
+  deleteFile,
+  getSignedUrl,
+} from 'pubsweet-components-faraday/src/redux/files'
+import {
+  FileItem,
+  FilePicker,
+} from 'pubsweet-components-faraday/src/components/Files'
+
 import {
   ConfirmationModal,
   withModal2,
@@ -57,6 +67,9 @@ const ReviewerReportForm = ({
   handleSubmit,
   formValues = {},
   review = {},
+  addFile,
+  removeFile,
+  previewFile,
 }) => (
   <Root>
     <Row>
@@ -81,10 +94,14 @@ const ReviewerReportForm = ({
       />
     </Row>
     <Spacing />
-    <Row>
-      <Label>
-        Report <ActionText left={12}>Upload file</ActionText>
-      </Label>
+    <Row left>
+      <Label>Report</Label>
+      <FilePicker
+        allowedFileExtensions={['pdf', 'doc', 'docx']}
+        onUpload={addFile}
+      >
+        <ActionText left={12}>Upload file</ActionText>
+      </FilePicker>
     </Row>
     <Row>
       <FullWidth>
@@ -102,6 +119,21 @@ const ReviewerReportForm = ({
         />
       </FullWidth>
     </Row>
+    {formValues.files && (
+      <Row>
+        {formValues.files.map(file => (
+          <FileItem
+            compact
+            id={file.id}
+            key={file.id}
+            {...file}
+            downloadFile={previewFile}
+            previewFile={previewFile}
+            removeFile={removeFile}
+          />
+        ))}
+      </Row>
+    )}
     {formValues.hasConfidential ? (
       <Fragment>
         <Row>
@@ -176,6 +208,9 @@ export default compose(
       getFormValues,
       createRecommendation,
       updateRecommendation,
+      uploadFile,
+      deleteFile,
+      getSignedUrl,
     },
   ),
   withProps(({ review = {} }) => ({
@@ -188,6 +223,44 @@ export default compose(
     changeField: ({ changeForm }) => (field, value) => {
       changeForm('reviewerReport', field, value)
     },
+    addFile: ({
+      formValues: { files = [] },
+      uploadFile,
+      changeForm,
+      version,
+    }) => file => {
+      uploadFile(file, 'review', version.id)
+        .then(file => {
+          const newFiles = [...files, file]
+
+          setTimeout(() => {
+            changeForm('reviewerReport', 'files', newFiles)
+          }, 1000)
+        })
+        .catch(e => console.error(`Couldn't upload file.`, e))
+    },
+    removeFile: ({
+      formValues: { files = [] },
+      changeForm,
+      deleteFile,
+    }) => id => e => {
+      deleteFile(id)
+        .then(r => {
+          const newFiles = files.filter(f => f.id !== id)
+          changeForm('reviewerReport', 'files', newFiles)
+        })
+        .catch(e => console.error(`Couldn't delete the file.`, e))
+
+      const newFiles = files.filter(f => f.id !== id)
+      changeForm('reviewerReport', 'files', newFiles)
+    },
+    previewFile: ({ getSignedUrl }) => fileId => e => {
+      e.preventDefault()
+      const windowReference = window.open()
+      getSignedUrl(fileId).then(({ signedUrl }) => {
+        windowReference.location = signedUrl
+      })
+    },
   }),
   reduxForm({
     form: 'reviewerReport',
@@ -266,7 +339,7 @@ const Row = styled.div`
   flex: 1;
   box-sizing: border-box;
   flex-wrap: wrap;
-  justify-content: space-between;
+  justify-content: ${({ left }) => (left ? 'left' : 'space-between')};
   ${defaultText};
 `
 
diff --git a/packages/components-faraday/src/components/Files/FileItem.js b/packages/components-faraday/src/components/Files/FileItem.js
index 618292b0234f5e626ec62ff2b520da834572d764..960781cb067ec9d5228709508a714466067b1cc0 100644
--- a/packages/components-faraday/src/components/Files/FileItem.js
+++ b/packages/components-faraday/src/components/Files/FileItem.js
@@ -1,6 +1,7 @@
-import React from 'react'
-import { Icon } from '@pubsweet/ui'
-import styled, { withTheme } from 'styled-components'
+import React, { Fragment } from 'react'
+import { last } from 'lodash'
+import { Icon, th } from '@pubsweet/ui'
+import styled, { withTheme, css } from 'styled-components'
 
 const parseFileSize = size => {
   const kbSize = size / 1000
@@ -17,6 +18,11 @@ const parseFileSize = size => {
   return `${size} bytes`
 }
 
+const hasPreview = (name = '') => {
+  const extension = last(name.split('.'))
+  return ['pdf', 'png', 'jpg'].includes(extension)
+}
+
 const FileItem = ({
   dragHandle,
   name,
@@ -24,42 +30,82 @@ const FileItem = ({
   id,
   removeFile,
   previewFile,
+  downloadFile,
+  compact = false,
   theme,
   ...rest
 }) => (
-  <Root data-test={`file-${id}`}>
-    {dragHandle}
-    <Info>
-      <span>{name}</span>
-      <span>{parseFileSize(size)}</span>
-    </Info>
-    <Buttons>
-      <button onClick={previewFile(id)}>
-        <Icon color={theme.colorPrimary} size={3}>
-          eye
-        </Icon>
-      </button>
-      <button onClick={removeFile(id)} title="Delete">
-        <Icon color={theme.colorPrimary} size={3}>
-          trash-2
-        </Icon>
-      </button>
-    </Buttons>
-  </Root>
+  <Fragment>
+    {compact ? (
+      <FileRoot data-test={`file-${id}`}>
+        {hasPreview(name) && (
+          <IconButton onClick={previewFile(id)}>
+            <Icon primary size={3}>
+              eye
+            </Icon>
+          </IconButton>
+        )}
+        {downloadFile && (
+          <IconButton onClick={downloadFile(id, name)}>
+            <Icon primary size={3}>
+              download
+            </Icon>
+          </IconButton>
+        )}
+        <FileName>{name}</FileName>
+        <FileSize>{parseFileSize(size)}</FileSize>
+        {removeFile && (
+          <IconButton onClick={removeFile(id)}>
+            <Icon primary size={3}>
+              trash-2
+            </Icon>
+          </IconButton>
+        )}
+      </FileRoot>
+    ) : (
+      <Root data-test={`file-${id}`}>
+        {dragHandle}
+        <Info>
+          <span>{name}</span>
+          <span>{parseFileSize(size)}</span>
+        </Info>
+        <Buttons>
+          <button onClick={previewFile(id)}>
+            <Icon color={theme.colorPrimary} size={3}>
+              eye
+            </Icon>
+          </button>
+          {removeFile && (
+            <button onClick={removeFile(id)} title="Delete">
+              <Icon color={theme.colorPrimary} size={3}>
+                trash-2
+              </Icon>
+            </button>
+          )}
+        </Buttons>
+      </Root>
+    )}
+  </Fragment>
 )
 
 export default withTheme(FileItem)
 
 // #region styles
+const defaultText = css`
+  color: ${th('colorPrimary')};
+  font-family: ${th('fontHeading')};
+  font-size: ${th('fontSizeBaseSmall')};
+`
+
 const Root = styled.div`
   align-items: center;
-  border: ${({ theme }) => theme.borderDefault};
+  border: ${th('borderDefault')};
   display: flex;
   margin: 5px;
 `
 
 const Info = styled.div`
-  border-right: ${({ theme }) => theme.borderDefault};
+  border-right: ${th('borderDefault')};
   display: flex;
   flex: 1;
   justify-content: space-between;
@@ -88,4 +134,31 @@ const Buttons = styled.div`
     }
   }
 `
+const FileName = styled.span`
+  ${defaultText};
+  margin: 0 ${th('subGridUnit')};
+`
+const FileSize = FileName.extend`
+  margin-left: ${th('subGridUnit')};
+`
+
+const IconButton = styled.div`
+  align-items: center;
+  cursor: pointer;
+  display: flex;
+  justify-content: center;
+  margin: 0 ${th('subGridUnit')};
+  &:hover {
+    opacity: 0.7;
+  }
+`
+
+const FileRoot = styled.div`
+  align-items: center;
+  border: ${th('borderDefault')};
+  display: flex;
+  flex-direction: row;
+  margin-bottom: ${th('subGridUnit')};
+  padding: ${th('subGridUnit')};
+`
 // #endregion
diff --git a/packages/components-faraday/src/components/Files/index.js b/packages/components-faraday/src/components/Files/index.js
index 6df727395c45e4df9e5db1d031a4d43146ad8440..ab85f18a55158ad1fc24c64e32e0023158ec7ae2 100644
--- a/packages/components-faraday/src/components/Files/index.js
+++ b/packages/components-faraday/src/components/Files/index.js
@@ -1 +1,3 @@
 export { default as Files } from './Files'
+export { default as FileItem } from './FileItem'
+export { default as FilePicker } from './FilePicker'
diff --git a/packages/xpub-faraday/config/upload-validations.js b/packages/xpub-faraday/config/upload-validations.js
index 23fa37d7d5a7b0403731429eb910f5f402799000..21b2412ef38c1f8aea0cf3f3e98faf486a3b7107 100644
--- a/packages/xpub-faraday/config/upload-validations.js
+++ b/packages/xpub-faraday/config/upload-validations.js
@@ -16,4 +16,5 @@ module.exports = {
       'application/msword',
     ])
     .error(new Error('Only Word documents and PDFs are allowed')),
+  review: Joi.any(),
 }