diff --git a/demo/src/index.js b/demo/src/index.js
index 3f7793bdd0274e9d20e8096a9784c9ee96fa847c..d501089b0f113d226c66c6bbedfa278a2175b976 100644
--- a/demo/src/index.js
+++ b/demo/src/index.js
@@ -9,6 +9,7 @@ const config = {
   annotation: {
     adapter: (canvasId) => new LocalStorageAdapter(`localStorage://?canvasId=${canvasId}`),
     // adapter: (canvasId) => new AnnototAdapter(canvasId, endpointUrl),
+    // downloadCanvasAnnotations: true,
   },
   id: 'demo',
   window: {
diff --git a/src/AnnotationDownloadDialog.js b/src/AnnotationDownloadDialog.js
new file mode 100644
index 0000000000000000000000000000000000000000..dc1241e188c9a14f9360a7f8040684c077ccda02
--- /dev/null
+++ b/src/AnnotationDownloadDialog.js
@@ -0,0 +1,110 @@
+import React, { Component } from 'react';
+import Dialog from '@material-ui/core/Dialog';
+import DialogContent from '@material-ui/core/DialogContent';
+import DialogTitle from '@material-ui/core/DialogTitle';
+import GetAppIcon from '@material-ui/icons/GetApp';
+import List from '@material-ui/core/List'
+import ListItem from '@material-ui/core/ListItem'
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import ListItemText from '@material-ui/core/ListItemText'
+import Typography from '@material-ui/core/Typography';
+import PropTypes, { bool } from 'prop-types';
+
+export class AnnotationDownloadDialog extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      downloadLinks: [],
+    }
+  }
+
+  componentDidUpdate(prevProps) {
+    const { canvases, config, open } = this.props;
+    const { open: prevOpen } = prevProps || {};
+    if (prevOpen !== open && open) {
+      const reducer = async (acc, canvas) => {
+        const store = config.annotation.adapter(canvas.id);
+        const _acc = await acc;
+        const content = await store.all();
+        if (content
+          ) {
+          const label = canvas.__jsonld.label || canvas.id;
+          const data = new Blob([JSON.stringify(content)], { type: 'application/json' })
+          const url = window.URL.createObjectURL(data);
+          return [..._acc, {
+            id: content.id || content['@id'],
+            canvasId: canvas.id,
+            label,
+            url,
+          }]
+        }
+        return _acc;
+      }
+      if (canvases && canvases.length > 0) {
+        canvases.reduce(reducer, []).then(downloadLinks => {
+          this.setState({ downloadLinks })
+        });
+      } else {
+        this.setState({ downloadLinks: [] });
+      }
+    }
+    if (prevOpen !== open && !open) {
+      this.setState({ downloadLinks: [] });
+    }
+  }
+
+  render() {
+    const { canvases, config, handleClose, open } = this.props;
+    return (
+      <Dialog
+        aria-labelledby="annotation-download-dialog-title"
+        id="annotation-download-dialog"
+        onClose={handleClose}
+        onEscapeKeyDown={handleClose}
+        open={open}
+      >
+        <DialogTitle id="annotation-download-dialog-title" disableTypography>
+          <Typography variant="h2">Download Annotations</Typography>
+        </DialogTitle>
+        <DialogContent>
+          { this.state.downloadLinks === undefined || this.state.downloadLinks.length === 0 ? (
+            <Typography variant="body1">No annotations stored yet.</Typography>
+          ) : (
+            <List>
+              {this.state.downloadLinks.map(dl => (
+              <ListItem button
+                component="a"
+                key={dl.canvasId}
+                aria-label={`Download annotations for ${dl.label}`}
+                startIcon={<GetAppIcon/>}
+                href={dl.url}
+                download={`annotations-${dl.id}.json`}
+              >
+                <ListItemIcon>
+                  <GetAppIcon/>
+                </ListItemIcon>
+                <ListItemText>
+                  Download annotations for {dl.label}
+                </ListItemText>
+              </ListItem>
+              ))}
+            </List>
+          )}
+        </DialogContent>
+      </Dialog>
+    )
+  }
+}
+
+AnnotationDownloadDialog.propTypes = {
+  canvases: PropTypes.arrayOf(
+    PropTypes.shape({ id: PropTypes.string, index: PropTypes.number }),
+  ),
+  config: PropTypes.shape({
+    annotation: PropTypes.shape({
+      adapter: PropTypes.func,
+    }),
+  }),
+  handleClose: PropTypes.func.isRequired,
+  open: bool.isRequired,
+}
diff --git a/src/plugins/miradorAnnotationPlugin.js b/src/plugins/miradorAnnotationPlugin.js
index 9ebeec69b9daceea4768575206e6f5ed08ede9f0..c4a60383aa16cf6b289135e417279f1748b6ab43 100644
--- a/src/plugins/miradorAnnotationPlugin.js
+++ b/src/plugins/miradorAnnotationPlugin.js
@@ -2,14 +2,21 @@ import React, { Component } from 'react';
 import PropTypes from 'prop-types';
 import * as actions from 'mirador/dist/es/src/state/actions';
 import AddBoxIcon from '@material-ui/icons/AddBox';
+import GetAppIcon from '@material-ui/icons/GetApp';
 import { MiradorMenuButton } from 'mirador/dist/es/src/components/MiradorMenuButton';
+import { getVisibleCanvases } from 'mirador/dist/es/src/state/selectors/canvases';
+import { AnnotationDownloadDialog } from '../AnnotationDownloadDialog';
 
 /** */
 class MiradorAnnotation extends Component {
   /** */
   constructor(props) {
     super(props);
+    this.state = {
+      annotationDownloadDialogOpen: false,
+    };
     this.openCreateAnnotationCompanionWindow = this.openCreateAnnotationCompanionWindow.bind(this);
+    this.toggleCanvasDownloadDialog = this.toggleCanvasDownloadDialog.bind(this);
   }
 
   /** */
@@ -23,9 +30,17 @@ class MiradorAnnotation extends Component {
     });
   }
 
+  toggleCanvasDownloadDialog(e) {
+    const newState = {
+      annotationDownloadDialogOpen: !this.state.annotationDownloadDialogOpen,
+    }
+    this.setState(newState);
+  }
+
   /** */
   render() {
-    const { TargetComponent, targetProps } = this.props;
+    const { canvases, config, TargetComponent, targetProps } = this.props;
+    const showAnnotationDownloadDialog = config.annotation && config.annotation.downloadCanvasAnnotations;
     return (
       <div>
         <TargetComponent
@@ -38,6 +53,23 @@ class MiradorAnnotation extends Component {
         >
           <AddBoxIcon />
         </MiradorMenuButton>
+        { showAnnotationDownloadDialog && (
+          <MiradorMenuButton
+            aria-label="Download annotation page for canvas"
+            onClick={this.toggleCanvasDownloadDialog}
+            size="small"
+          >
+            <GetAppIcon />
+          </MiradorMenuButton>
+        )}
+        { showAnnotationDownloadDialog && (
+          <AnnotationDownloadDialog
+            canvases={canvases}
+            config={config}
+            handleClose={this.toggleCanvasDownloadDialog}
+            open={this.state.annotationDownloadDialogOpen}
+          />
+        )}
       </div>
     );
   }
@@ -45,6 +77,15 @@ class MiradorAnnotation extends Component {
 
 MiradorAnnotation.propTypes = {
   addCompanionWindow: PropTypes.func.isRequired,
+  canvases: PropTypes.arrayOf(
+    PropTypes.shape({ id: PropTypes.string, index: PropTypes.number }),
+  ),
+  config: PropTypes.shape({
+    annotation: PropTypes.shape({
+      adapter: PropTypes.func,
+      downloadCanvasAnnotations: PropTypes.bool,
+    }),
+  }),
   TargetComponent: PropTypes.oneOfType([
     PropTypes.func,
     PropTypes.node,
@@ -59,9 +100,17 @@ const mapDispatchToProps = (dispatch, props) => ({
   ),
 });
 
+const mapStateToProps = function mapStateToProps(state, { targetProps: { windowId } }) {
+  return {
+    canvases: getVisibleCanvases(state, { windowId }),
+    config: state.config,
+  };
+}
+
 export default {
   component: MiradorAnnotation,
   mapDispatchToProps,
+  mapStateToProps,
   mode: 'wrap',
   target: 'AnnotationSettings',
 };