diff --git a/app/scripts/components/analysis/results/chart-card.tsx b/app/scripts/components/analysis/results/chart-card.tsx
index edd7b6694..a47520eb6 100644
--- a/app/scripts/components/analysis/results/chart-card.tsx
+++ b/app/scripts/components/analysis/results/chart-card.tsx
@@ -1,4 +1,10 @@
-import React, { useCallback, useRef, useMemo, useState } from 'react';
+import React, {
+  useCallback,
+  useRef,
+  useMemo,
+  useState,
+  MouseEvent
+} from 'react';
 import { format } from 'date-fns';
 import { reverse } from 'd3';
 import { useTheme } from 'styled-components';
@@ -11,6 +17,7 @@ import {
   CollecticonCircleInformation,
   CollecticonDownload2
 } from '@devseed-ui/collecticons';
+import { Dropdown, DropMenu, DropTitle } from '@devseed-ui/dropdown';
 
 import { TimeseriesData } from './timeseries-data';
 import {
@@ -32,6 +39,8 @@ import { ChartLoading } from '$components/common/loading-skeleton';
 import { dateFormatter } from '$components/common/chart/utils';
 import { Tip } from '$components/common/tip';
 import { composeVisuallyDisabled } from '$utils/utils';
+import { exportCsv } from '$components/common/chart/analysis/utils';
+import DropMenuItemButton from '$styles/drop-menu-item-button';
 
 interface ChartCardProps {
   title: React.ReactNode;
@@ -64,22 +73,37 @@ export default function ChartCard(props: ChartCardProps) {
   const [brushIndex, setBrushIndex] = useState({ startIndex: 0, endIndex: 0 });
   const noDownloadReason = getNoDownloadReason(chartData);
 
-  const onExportClick = useCallback(() => {
-    if (!chartData.data?.timeseries.length) {
-      return;
-    }
+  const onExportClick = useCallback(
+    (e: MouseEvent, type: 'image' | 'text') => {
+      e.preventDefault();
+      if (!chartData.data?.timeseries.length) {
+        return;
+      }
 
-    const { startIndex, endIndex } = brushIndex;
-    // The indexes expect the data to be ascending, so we have to reverse the
-    // data.
-    const data = reverse(chartData.data.timeseries);
-    const dFormat = 'yyyy-MM-dd';
-    const startDate = format(new Date(data[startIndex].date), dFormat);
-    const endDate = format(new Date(data[endIndex].date), dFormat);
+      const { startIndex, endIndex } = brushIndex;
+      // The indexes expect the data to be ascending, so we have to reverse the
+      // data.
+      const data = reverse(chartData.data.timeseries);
+      const dFormat = 'yyyy-MM-dd';
+      const startDate = format(new Date(data[startIndex].date), dFormat);
+      const endDate = format(new Date(data[endIndex].date), dFormat);
 
-    const filename = `chart.${id}.${startDate}-${endDate}`;
-    chartRef.current?.saveAsImage(filename);
-  }, [id, chartData.data, brushIndex]);
+      const filename = `chart.${id}.${startDate}-${endDate}`;
+
+      if (type === 'image') {
+        chartRef.current?.saveAsImage(filename);
+      } else {
+        exportCsv(
+          filename,
+          data,
+          data[startIndex].date,
+          data[endIndex].date,
+          activeMetrics
+        );
+      }
+    },
+    [id, chartData.data, brushIndex, activeMetrics]
+  );
 
   const theme = useTheme();
 
@@ -109,19 +133,41 @@ export default function ChartCard(props: ChartCardProps) {
         </CardHeadline>
         <CardActions>
           <Toolbar size='small'>
-            <Tip
-              content={noDownloadReason}
-              disabled={!noDownloadReason}
-              hideOnClick={false}
+            <Dropdown
+              alignment='right'
+              triggerElement={(props) => (
+                <Tip
+                  content={noDownloadReason}
+                  disabled={!noDownloadReason}
+                  hideOnClick={false}
+                >
+                  <ChartDownloadButton
+                    {...props}
+                    variation='base-text'
+                    visuallyDisabled={!!noDownloadReason}
+                  >
+                    <CollecticonDownload2 title='Download' meaningful />
+                  </ChartDownloadButton>
+                </Tip>
+              )}
             >
-              <ChartDownloadButton
-                variation='base-text'
-                onClick={onExportClick}
-                visuallyDisabled={!!noDownloadReason}
-              >
-                <CollecticonDownload2 title='Download' meaningful />
-              </ChartDownloadButton>
-            </Tip>
+              <DropTitle>Select a file format</DropTitle>
+              <DropMenu>
+                <li>
+                  <DropMenuItemButton
+                    onClick={(e) => onExportClick(e, 'image')}
+                  >
+                    Image (JPG)
+                  </DropMenuItemButton>
+                </li>
+                <li>
+                  <DropMenuItemButton onClick={(e) => onExportClick(e, 'text')}>
+                    Text (CSV)
+                  </DropMenuItemButton>
+                </li>
+              </DropMenu>
+            </Dropdown>
+
             <VerticalDivider variation='dark' />
             <ToolbarIconButton variation='base-text'>
               <CollecticonCircleInformation title='More info' meaningful />
diff --git a/app/scripts/components/common/chart/analysis/utils.ts b/app/scripts/components/common/chart/analysis/utils.ts
index d87046886..2d80734de 100644
--- a/app/scripts/components/common/chart/analysis/utils.ts
+++ b/app/scripts/components/common/chart/analysis/utils.ts
@@ -1,8 +1,12 @@
 import { RefObject, Component } from 'react';
+import FileSaver from 'file-saver';
+import { unparse } from 'papaparse';
 import {
   chartAspectRatio,
   brushHeight
 } from '$components/common/chart/constant';
+import { TimeseriesDataUnit } from '$components/analysis/results/timeseries-data';
+import { DataMetric } from '$components/analysis/results/analysis-head-actions';
 
 const URL = window.URL;
 
@@ -202,3 +206,25 @@ export async function exportImage({
     throw Error('No SVG specified');
   }
 }
+
+export function exportCsv(
+  filename: string,
+  data: TimeseriesDataUnit[],
+  startDate: string,
+  endDate: string,
+  activeMetrics: DataMetric[]
+) {
+  const startTimestamp = +new Date(startDate);
+  const endTimestamp = +new Date(endDate);
+  const filtered = data.filter((row) => {
+    const timestamp = +new Date(row.date);
+    return timestamp >= startTimestamp && timestamp <= endTimestamp;
+  });
+  const csv = unparse(filtered, {
+    columns: ['date', ...activeMetrics.map((m) => m.id)]
+  });
+  FileSaver.saveAs(
+    new Blob([csv], { type: 'text/csv;charset=utf-8' }),
+    `${filename}.csv`
+  );
+}
diff --git a/package.json b/package.json
index 499d6f48b..705600375 100644
--- a/package.json
+++ b/package.json
@@ -124,6 +124,7 @@
     "mapbox-gl": "^2.9.2",
     "mapbox-gl-compare": "^0.4.0",
     "mapbox-gl-draw-rectangle-mode": "^1.0.4",
+    "papaparse": "^5.3.2",
     "polished": "^4.1.3",
     "prop-types": "^15.7.2",
     "pure-react-carousel": "^1.28.1",
diff --git a/yarn.lock b/yarn.lock
index 5974d4011..f60f500cb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9533,6 +9533,11 @@ p-try@^2.0.0:
   resolved "http://ec2-3-82-125-171.compute-1.amazonaws.com:4873/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
   integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
 
+papaparse@^5.3.2:
+  version "5.3.2"
+  resolved "http://ec2-3-82-125-171.compute-1.amazonaws.com:4873/papaparse/-/papaparse-5.3.2.tgz#d1abed498a0ee299f103130a6109720404fbd467"
+  integrity sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw==
+
 "parcel-resolver-thematics@link:./parcel-resolver-thematics":
   version "0.0.0"
   uid ""