diff --git a/frontend/composables/useDownloadBlob.ts b/frontend/composables/useDownloadBlob.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7ae7b07beab9547b5e2e9d6ae992271f03a01a1e
--- /dev/null
+++ b/frontend/composables/useDownloadBlob.ts
@@ -0,0 +1,14 @@
+export function useDownloadBlob() {
+    function download(blob: MaybeRef<Blob>, filename: MaybeRef<string>) {
+        const toValueBlob = toValue(blob)
+        const toValueFilename = toValue(filename)
+        var a = document.createElement("a");
+        a.href = URL.createObjectURL(toValueBlob);
+        a.download = toValueFilename;
+        a.click();
+        URL.revokeObjectURL(a.href);
+    }
+
+    return { download }
+
+}
\ No newline at end of file
diff --git a/frontend/composables/useRasterize.ts b/frontend/composables/useRasterize.ts
new file mode 100644
index 0000000000000000000000000000000000000000..02d9ac80115e764f0cdb3813cfdde489ff71117d
--- /dev/null
+++ b/frontend/composables/useRasterize.ts
@@ -0,0 +1,44 @@
+import { useSerialize } from './useSerialize';
+import { useSvgPlot } from './useSvgPlot';
+const { serialize } = useSerialize()
+
+export function useRasterize() {
+
+    function rasterize(component: MaybeRef<ComponentPublicInstance | null>, filename: MaybeRef<string>) {
+        const toValueCompo = toValue(component)
+
+        if (toValueCompo !== null) {
+            const { svg } = useSvgPlot(toValueCompo)
+            const toValueSvg = toValue(svg)
+            console.log(toValueSvg)
+            if (toValueSvg !== null) {
+                let resolve, reject;
+                const promise: Promise<Blob> = new Promise((y, n) => (resolve = y, reject = n));
+                const image = new Image;
+                image.onerror = reject;
+
+                image.onload = () => {
+                    const rect = toValueSvg.getBoundingClientRect();
+                    const canvas = document.createElement("canvas");
+                    canvas.width = rect.width
+                    canvas.height = rect.height
+                    const ctx = canvas.getContext("2d")
+
+
+                    if (ctx !== null) {
+                        ctx.drawImage(image, 0, 0, rect.width, rect.height);
+                        ctx.canvas.toBlob(resolve);
+                    }
+                }
+                const blob = toValue(serialize(component))
+                if (blob !== undefined) {
+                    image.src = URL.createObjectURL(blob);
+                    console.log(image.src)
+                }
+                return promise;
+            }
+        }
+    }
+    return { rasterize }
+
+}
\ No newline at end of file
diff --git a/frontend/composables/useSerialize.ts b/frontend/composables/useSerialize.ts
new file mode 100644
index 0000000000000000000000000000000000000000..47bcbc451f39d721fb4e6527bb2f77303c66bbad
--- /dev/null
+++ b/frontend/composables/useSerialize.ts
@@ -0,0 +1,37 @@
+import { useSvgPlot } from './useSvgPlot';
+
+
+export function useSerialize() {
+    const xmlns = ref("http://www.w3.org/2000/xmlns/");
+    const xlinkns = ref("http://www.w3.org/1999/xlink");
+    const svgns = ref("http://www.w3.org/2000/svg");
+    const blob = ref<Blob>()
+
+    function serialize(compo: MaybeRef<ComponentPublicInstance | null>) {
+        const toValueCompo = toValue(compo)
+        if (toValueCompo !== null) {
+            const { svg } = useSvgPlot(toValueCompo)
+            const toValueSvg = toValue(svg)
+            if (toValueSvg !== null) {
+                const clonedSvg = toValueSvg.cloneNode(true);
+                const fragment = window.location.href + "#";
+                const walker = document.createTreeWalker(toValueSvg, NodeFilter.SHOW_ELEMENT);
+                while (walker.nextNode()) {
+                    for (const attr of walker.currentNode.attributes) {
+                        if (attr.value.includes(fragment)) {
+                            attr.value = attr.value.replace(fragment, "#");
+                        }
+                    }
+                }
+                clonedSvg.setAttributeNS(xmlns.value, "xmlns", svgns.value);
+                clonedSvg.setAttributeNS(xmlns.value, "xmlns:xlink", xlinkns.value);
+                const serializer = new window.XMLSerializer;
+                const string = serializer.serializeToString(clonedSvg);
+                blob.value = new Blob([string], { type: "image/svg+xml" });
+                return blob
+            }
+            else { return undefined }
+        }
+    }
+    return { serialize }
+}
\ No newline at end of file
diff --git a/frontend/composables/useSvgPlot.ts b/frontend/composables/useSvgPlot.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b2375d3a89bacfd8b7cc66451c2653fbdee38a91
--- /dev/null
+++ b/frontend/composables/useSvgPlot.ts
@@ -0,0 +1,9 @@
+export function useSvgPlot(component: MaybeRef<ComponentPublicInstance>) {
+    const svg = ref<SVGElement | null>(null)
+
+    const toValueCompo = toValue(component)
+    const rootElem = toValueCompo.$el
+    svg.value = rootElem.querySelector("svg")
+    console.log(svg.value)
+    return { svg }
+}
\ No newline at end of file
diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts
index 84546c275c099b2506e6bd0a207fd5444f8be64a..46bcdc729132107c84a60172f4db4c6bea74c71b 100644
--- a/frontend/nuxt.config.ts
+++ b/frontend/nuxt.config.ts
@@ -40,11 +40,11 @@ export default defineNuxtConfig({
     defaultLocale: 'en', // not needed if you have @nuxtjs/i18n installed
   },
   security: {
-    // csrf: {
-    //   // https: false,
-    //   addCsrfTokenToEventCtx: true,
-    //   cookieKey: 'csrftoken',
-    // }
+    headers: {
+      contentSecurityPolicy: {
+        "img-src": ["'self'", "data:", "blob:"]
+      }
+    }
   },
   vuetify: {
     vuetifyOptions: {
diff --git a/frontend/pages/analyses/[analysisId].vue b/frontend/pages/analyses/[analysisId].vue
index bf017bb4fa8e175e1cf2f277e1c1749ac29ca06a..43bcdb5225dbd3ed494924104ebeda752a04ddae 100644
--- a/frontend/pages/analyses/[analysisId].vue
+++ b/frontend/pages/analyses/[analysisId].vue
@@ -7,12 +7,18 @@ import { useRoute, computed } from "#imports";
 import * as d3 from "d3";
 import { useElementSize } from '@vueuse/core'
 import { joinURL } from "ufo";
+import type { ComponentPublicInstance } from 'vue'
+import { useSerialize } from '~/composables/useSerialize';
+import { useRasterize } from '~/composables/useRasterize';
+import { useDownloadBlob } from '~/composables/useDownloadBlob';
 
 const route = useRoute();
 const { selectedProtein } = useSelectedProtein()
 
 const runtimeConfig = useRuntimeConfig()
-
+const { serialize } = useSerialize()
+const { rasterize } = useRasterize()
+const { download } = useDownloadBlob()
 
 const analysisId = computed(() => {
   if (Array.isArray(route.params.analysisId)) return null
@@ -164,7 +170,18 @@ const computedWidth = computed(() => {
 
 
 
+function downloadSvg(component: ComponentPublicInstance | null, filename: string) {
+  const blob = toValue(serialize(toValue(component)))
+  if (blob !== undefined) {
+    download(blob, filename)
+  }
+}
 
+async function downloadPng(component: ComponentPublicInstance | null, filename: string) {
+  const blob = await rasterize(toValue(component), filename)?.then((blob) => {
+    download(blob, filename)
+  })
+}
 
 watch(selectedProtein, () => {
   const toValHitId = toValue(selectedProtein)
@@ -191,7 +208,8 @@ const minRange = ref(0)
 const maxRange = ref(innerWidth.value)
 
 
-const svgRef = ref(null)
+const svgRef = ref<ComponentPublicInstance | null>(null)
+const figureRef = ref<ComponentPublicInstance | null>(null)
 
 const domain = ref([0, 10000])
 // const range = ref()
@@ -443,8 +461,31 @@ useHead({
     <v-card>
       <v-toolbar density="compact" class="pr-2">
         <v-toolbar-title>{{ analysis.name }}</v-toolbar-title>
-        <v-btn color="primary" prepend-icon="mdi-download" :href="getResultArchiveUrl(analysis.id)">Download
-          all results</v-btn>
+        <v-menu>
+          <template v-slot:activator="{ props }">
+            <v-btn color="primary" prepend-icon="md:download" class="mr-2" v-bind="props">
+              Export
+            </v-btn>
+          </template>
+          <v-list>
+            <v-list-item v-if="analysis !== null" prepend-icon="mdi-archive" value="archive">
+              <v-list-item-title @click="getResultArchiveUrl(analysis.id)">Download all results
+              </v-list-item-title>
+            </v-list-item>
+            <v-divider></v-divider>
+            <v-list-subheader>Images</v-list-subheader>
+            <v-list-item prepend-icon="mdi-svg" value="svg">
+              <v-list-item-title @click="downloadSvg(figureRef, `df-results-${analysis.name}.svg`)">to
+                svg</v-list-item-title>
+
+            </v-list-item>
+            <v-list-item prepend-icon="mdi-image" value="png">
+
+              <v-list-item-title @click="downloadPng(figureRef, `df-results-${analysis.name}.png`)">to
+                png</v-list-item-title>
+            </v-list-item>
+          </v-list>
+        </v-menu>
         <v-chip color="primary" rounded>{{ new Date(analysis.create_time).toLocaleString() }}</v-chip>
         <template v-if="analysis.percentage_done !== 100 && analysis.stderr === ''" #extension>
           <v-row>
@@ -471,7 +512,7 @@ useHead({
       <template v-else>
         <v-card-text>
           <div ref="gbContainer">
-            <v-card flat color="transparent">
+            <v-card ref="figureRef" flat color="transparent">
               <v-toolbar variant="flat" density="compact" color="transparent">
                 <v-spacer></v-spacer><v-toolbar-items>
                   <v-switch v-model="displayHmmerHits" color="primary" label="Display HMM-only hits
diff --git a/frontend/pages/analyses/[analysisId]/genes.vue b/frontend/pages/analyses/[analysisId]/genes.vue
index 1841eddbbc4135bbd7821b5f62d145f2b861bf5e..49f4567e3472fb718090380532a768616e2fd9a2 100644
--- a/frontend/pages/analyses/[analysisId]/genes.vue
+++ b/frontend/pages/analyses/[analysisId]/genes.vue
@@ -50,11 +50,11 @@ const sanitizedGenes = computed(() => {
 </script>
 <template>
   <AnalysisResultDataTable v-if="sanitizedGenes.length > 0" :items="sanitizedGenes" :headers="headers" />
-    <v-card v-else flat color="transparent">
-      <v-card-text>
-        <v-alert type="info" variant="tonal">
-          No gene found
-        </v-alert>
-      </v-card-text>
-    </v-card>
+  <v-card v-else flat color="transparent">
+    <v-card-text>
+      <v-alert type="info" variant="tonal">
+        No gene found
+      </v-alert>
+    </v-card-text>
+  </v-card>
 </template>
diff --git a/frontend/pages/analyses/[analysisId]/systems.vue b/frontend/pages/analyses/[analysisId]/systems.vue
index e8e45cc0b1bfdf79f2285c25c622fa9c1f24c8e7..8f3d8fc018f199d6a06e637dc4a897558dc8dc69 100644
--- a/frontend/pages/analyses/[analysisId]/systems.vue
+++ b/frontend/pages/analyses/[analysisId]/systems.vue
@@ -8,9 +8,6 @@ const { data: systems, error } = await useAPI<SystemsOut>(
   `/analysis/${route.params.analysisId}/systems`
 );
 
-
-
-
 if (error.value) {
   throw createError({ message: `Error while getting the list systems for analysis ${route.params.analysisId}` })
 }