diff --git a/packages/component-aws-download/src/FileBackend.js b/packages/component-aws-download/src/FileBackend.js
index 52a907927cb3fb1a3b20b7f1700285dc51495ed9..74c4a5a76f1d952ccc1405dcd90ac39b52e26718 100644
--- a/packages/component-aws-download/src/FileBackend.js
+++ b/packages/component-aws-download/src/FileBackend.js
@@ -21,37 +21,41 @@ const FileBackend = app => {
   app.get('/api/fileZip/:fragmentId', authBearer, async (req, res) => {
     const archive = archiver('zip')
     const { fragmentId } = req.params
+    const getObject = util.promisify(s3.getObject.bind(s3))
+    const listObjects = util.promisify(s3.listObjects.bind(s3))
 
-    archive.pipe(res)
-    res.attachment(`${fragmentId}-archive.zip`)
-
-    const params = {
-      Bucket: s3Config.bucket,
-      Prefix: `${fragmentId}`,
-    }
+    try {
+      archive.pipe(res)
+      res.attachment(`${fragmentId}-archive.zip`)
 
-    const listObjects = util.promisify(s3.listObjects.bind(s3))
-    const getObject = util.promisify(s3.getObject.bind(s3))
+      const params = {
+        Bucket: s3Config.bucket,
+        Prefix: `${fragmentId}`,
+      }
 
-    return listObjects(params).then(data => {
-      Promise.all(
-        data.Contents.map(content =>
+      const s3Items = await listObjects(params)
+      const s3Files = await Promise.all(
+        s3Items.Contents.map(content =>
           getObject({
             Bucket: s3Config.bucket,
             Key: content.Key,
           }),
         ),
-      ).then(files => {
-        files.forEach((file, index) => {
-          archive.append(file.Body, {
-            name: `${_.get(file, 'Metadata.filetype') ||
-              'supplementary'}/${_.get(file, 'Metadata.filename') ||
-              file.ETag}`,
-          })
+      )
+
+      s3Files.forEach(f => {
+        archive.append(f.Body, {
+          name: `${_.get(f, 'Metadata.filetype') || 'supplementary'}/${_.get(
+            f,
+            'Metadata.filename',
+          ) || f.ETag}`,
         })
-        archive.finalize()
       })
-    })
+
+      archive.finalize()
+    } catch (err) {
+      res.status(err.statusCode).json({ error: err.message })
+    }
   })
 }
 
diff --git a/packages/components-faraday/src/components/Dashboard/DashboardCard.js b/packages/components-faraday/src/components/Dashboard/DashboardCard.js
index 5c37210632998bb8f4dc152d678686d445ef3f5c..0b149c4e78ff85308716dfead4c4d7012bd53fe4 100644
--- a/packages/components-faraday/src/components/Dashboard/DashboardCard.js
+++ b/packages/components-faraday/src/components/Dashboard/DashboardCard.js
@@ -1,12 +1,13 @@
 import React from 'react'
+import { get } from 'lodash'
 import PropTypes from 'prop-types'
-import { connect } from 'react-redux'
-import { get, isEmpty } from 'lodash'
 import { Button, Icon } from '@pubsweet/ui'
 import styled, { css } from 'styled-components'
-import { compose, getContext, withHandlers } from 'recompose'
+import { compose, getContext } from 'recompose'
 
-import { parseVersion, getFilesURL } from './utils'
+import { parseVersion } from './utils'
+
+import ZipFiles from './ZipFiles'
 
 const DashboardCard = ({
   deleteProject,
@@ -15,15 +16,14 @@ const DashboardCard = ({
   version,
   showAbstractModal,
   journal,
-  getItems,
   ...rest
 }) => {
   const { submitted, title, type, version: vers } = parseVersion(version)
-  const files = getFilesURL(get(version, 'files'))
   const status = get(project, 'status') || 'Draft'
-  const hasFiles = !isEmpty(files)
   const abstract = get(version, 'metadata.abstract')
   const metadata = get(version, 'metadata')
+  const files = get(version, 'files')
+  const hasFiles = files ? Object.values(files).some(f => f.length > 0) : false
   const journalIssueType = journal.issueTypes.find(
     t => t.value === get(metadata, 'issue'),
   )
@@ -47,17 +47,11 @@ const DashboardCard = ({
           </ManuscriptInfo>
         </Left>
         <Right>
-          {/* <form onSubmit={getItems}>
-            <Icon>download</Icon>
-            <button type="submit">DOWNLOAD</button>
-          </form> */}
-          <ClickableIcon
-            disabled={!hasFiles}
-            // onClick={() => (hasFiles ? downloadAll(files) : null)}
-            onClick={getItems}
-          >
-            <Icon>download</Icon>
-          </ClickableIcon>
+          <ZipFiles disabled={!hasFiles} fragmentId={version.id}>
+            <ClickableIcon disabled={!hasFiles}>
+              <Icon>download</Icon>
+            </ClickableIcon>
+          </ZipFiles>
           <ClickableIcon onClick={() => deleteProject(project)}>
             <Icon>trash-2</Icon>
           </ClickableIcon>
@@ -122,35 +116,7 @@ const DashboardCard = ({
   ) : null
 }
 
-export default compose(
-  getContext({ journal: PropTypes.object }),
-  connect(state => ({
-    token: state.currentUser.user.token,
-  })),
-  withHandlers({
-    getItems: ({ version, token }) => () => {
-      const xhr = new XMLHttpRequest()
-      xhr.onreadystatechange = function onXhrStateChange() {
-        if (this.readyState === 4 && this.status === 200) {
-          const fileName = `${version.id}-archive.zip`
-          const f = new File([this.response], fileName, {
-            type: 'application/zip',
-          })
-          const url = URL.createObjectURL(f)
-          const a = document.createElement('a')
-          a.href = url
-          a.download = fileName
-          document.body.appendChild(a)
-          a.click()
-        }
-      }
-      xhr.open('GET', `${window.location.origin}/api/fileZip/${version.id}`)
-      xhr.responseType = 'blob'
-      xhr.setRequestHeader('Authorization', `Bearer ${token}`)
-      xhr.send()
-    },
-  }),
-)(DashboardCard)
+export default compose(getContext({ journal: PropTypes.object }))(DashboardCard)
 
 // #region styled-components
 const defaultText = css`
diff --git a/packages/components-faraday/src/components/Dashboard/ZipFiles.js b/packages/components-faraday/src/components/Dashboard/ZipFiles.js
new file mode 100644
index 0000000000000000000000000000000000000000..8f6e9ece1807e90e2ffa9ed214c47ed49e3c99e2
--- /dev/null
+++ b/packages/components-faraday/src/components/Dashboard/ZipFiles.js
@@ -0,0 +1,103 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import { connect } from 'react-redux'
+import styled from 'styled-components'
+import { compose, withHandlers, withState } from 'recompose'
+
+import { Spinner } from '../UIComponents/index'
+
+const createAnchorElement = (file, filename) => {
+  const url = URL.createObjectURL(file)
+  const a = document.createElement('a')
+
+  a.href = url
+  a.download = filename
+  document.body.appendChild(a)
+
+  return {
+    a,
+    url,
+  }
+}
+
+const removeAnchorElement = (a, url) => {
+  document.body.removeChild(a)
+  URL.revokeObjectURL(url)
+}
+
+const ZipFiles = ({
+  disabled,
+  fragmentId,
+  fetching,
+  children,
+  downloadFiles,
+}) => (
+  <Root onClick={!disabled ? downloadFiles : null}>
+    {fetching ? <Spinner /> : children}
+  </Root>
+)
+
+const cache = {}
+
+const Zip = compose(
+  connect(state => ({
+    token: state.currentUser.user.token,
+  })),
+  withState('fetching', 'setFetching', false),
+  withHandlers({
+    downloadFiles: ({ fragmentId, token, setFetching, archiveName }) => () => {
+      if (cache[fragmentId]) {
+        const fileName = archiveName || `${fragmentId}-archive.zip`
+
+        const { a, url } = createAnchorElement(cache[fragmentId], fileName)
+        a.click()
+        removeAnchorElement(a, url)
+      } else {
+        setFetching(fetching => true)
+        const xhr = new XMLHttpRequest()
+        xhr.onreadystatechange = function onXhrStateChange() {
+          if (this.readyState === 4) {
+            setFetching(fetching => false)
+            if (this.status >= 200 && this.status < 300) {
+              const fileName = archiveName || `${fragmentId}-archive.zip`
+              const f = new File([this.response], fileName, {
+                type: 'application/zip',
+              })
+              cache[fragmentId] = f
+
+              const { a, url } = createAnchorElement(f, fileName)
+              a.click()
+              removeAnchorElement(a, url)
+            }
+          }
+        }
+        xhr.open('GET', `${window.location.origin}/api/fileZip/${fragmentId}`)
+        xhr.responseType = 'blob'
+        xhr.setRequestHeader('Authorization', `Bearer ${token}`)
+        xhr.send()
+      }
+    },
+  }),
+)(ZipFiles)
+
+Zip.propTypes = {
+  disabled: PropTypes.bool,
+  archiveName: PropTypes.string,
+  fragmentId: PropTypes.string.isRequired,
+}
+
+Zip.defaultProps = {
+  disabled: false,
+}
+
+export default Zip
+
+// #region styled components
+const Root = styled.div`
+  align-items: center;
+  cursor: pointer;
+  display: flex;
+  margin: 0 7px;
+  width: 38px;
+`
+// #endregion
diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js
index 71f7672f6f881a00488228551783c2093628de64..27413d8af8bb12feb820b5089e8ca04144f264b3 100644
--- a/packages/xpub-faraday/config/default.js
+++ b/packages/xpub-faraday/config/default.js
@@ -34,7 +34,7 @@ module.exports = {
   'pubsweet-client': {
     API_ENDPOINT: '/api',
     'login-redirect': '/',
-    'redux-log': false,
+    'redux-log': true,
     theme: process.env.PUBSWEET_THEME,
   },
   'mail-transport': {