diff --git a/app/client/api.ts b/app/client/api.ts
index a966d733..fb829f97 100644
--- a/app/client/api.ts
+++ b/app/client/api.ts
@@ -1,5 +1,5 @@
 import { ACCESS_CODE_PREFIX } from "../constant";
-import { ModelConfig, ModelType, useAccessStore } from "../store";
+import { ChatMessage, ModelConfig, ModelType, useAccessStore } from "../store";
 import { ChatGPTApi } from "./platforms/openai";
 
 export const ROLES = ["system", "user", "assistant"] as const;
@@ -54,6 +54,41 @@ export class ClientApi {
   prompts() {}
 
   masks() {}
+
+  async share(messages: ChatMessage[], avatarUrl: string | null = null) {
+    const msgs = messages
+      .map((m) => ({
+        from: m.role === "user" ? "human" : "gpt",
+        value: m.content,
+      }))
+      .concat([
+        {
+          from: "human",
+          value:
+            "Share from [ChatGPT Next Web]: https://github.com/Yidadaa/ChatGPT-Next-Web",
+        },
+      ]);
+    // 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用
+    // Please do not modify this message
+
+    console.log("[Share]", msgs);
+    const res = await fetch("/sharegpt", {
+      body: JSON.stringify({
+        avatarUrl,
+        items: msgs,
+      }),
+      headers: {
+        "Content-Type": "application/json",
+      },
+      method: "POST",
+    });
+
+    const resJson = await res.json();
+    console.log("[Share]", resJson);
+    if (resJson.id) {
+      return `https://shareg.pt/${resJson.id}`;
+    }
+  }
 }
 
 export const api = new ClientApi();
diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx
index a9a1071d..10d5af99 100644
--- a/app/components/exporter.tsx
+++ b/app/components/exporter.tsx
@@ -12,14 +12,17 @@ import ShareIcon from "../icons/share.svg";
 import BotIcon from "../icons/bot.png";
 
 import DownloadIcon from "../icons/download.svg";
-import { useMemo, useRef, useState } from "react";
+import { useEffect, useMemo, useRef, useState } from "react";
 import { MessageSelector, useMessageSelector } from "./message-selector";
 import { Avatar } from "./emoji";
 import dynamic from "next/dynamic";
 import NextImage from "next/image";
 
-import { toBlob, toPng } from "html-to-image";
+import { toBlob, toJpeg, toPng } from "html-to-image";
 import { DEFAULT_MASK_AVATAR } from "../store/mask";
+import { api } from "../client/api";
+import { prettyObject } from "../utils/format";
+import { EXPORT_MESSAGE_CLASS_NAME } from "../constant";
 
 const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
   loading: () => <LoadingIcon />,
@@ -214,37 +217,127 @@ export function MessageExporter() {
   );
 }
 
+export function RenderExport(props: {
+  messages: ChatMessage[];
+  onRender: (messages: ChatMessage[]) => void;
+}) {
+  const domRef = useRef<HTMLDivElement>(null);
+
+  useEffect(() => {
+    if (!domRef.current) return;
+    const dom = domRef.current;
+    const messages = Array.from(
+      dom.getElementsByClassName(EXPORT_MESSAGE_CLASS_NAME),
+    );
+
+    if (messages.length !== props.messages.length) {
+      return;
+    }
+
+    const renderMsgs = messages.map((v) => {
+      const [_, role] = v.id.split(":");
+      return {
+        role: role as any,
+        content: v.innerHTML,
+        date: "",
+      };
+    });
+
+    props.onRender(renderMsgs);
+  });
+
+  return (
+    <div ref={domRef}>
+      {props.messages.map((m, i) => (
+        <div
+          key={i}
+          id={`${m.role}:${i}`}
+          className={EXPORT_MESSAGE_CLASS_NAME}
+        >
+          <Markdown content={m.content} defaultShow />
+        </div>
+      ))}
+    </div>
+  );
+}
+
 export function PreviewActions(props: {
   download: () => void;
   copy: () => void;
   showCopy?: boolean;
+  messages?: ChatMessage[];
 }) {
+  const [loading, setLoading] = useState(false);
+  const [shouldExport, setShouldExport] = useState(false);
+
+  const onRenderMsgs = (msgs: ChatMessage[]) => {
+    setShouldExport(false);
+
+    api
+      .share(msgs)
+      .then((res) => {
+        if (!res) return;
+        copyToClipboard(res);
+        setTimeout(() => {
+          window.open(res, "_blank");
+        }, 800);
+      })
+      .catch((e) => {
+        console.error("[Share]", e);
+        showToast(prettyObject(e));
+      })
+      .finally(() => setLoading(false));
+  };
+
+  const share = async () => {
+    if (props.messages?.length) {
+      setLoading(true);
+      setShouldExport(true);
+    }
+  };
+
   return (
-    <div className={styles["preview-actions"]}>
-      {props.showCopy && (
+    <>
+      <div className={styles["preview-actions"]}>
+        {props.showCopy && (
+          <IconButton
+            text={Locale.Export.Copy}
+            bordered
+            shadow
+            icon={<CopyIcon />}
+            onClick={props.copy}
+          ></IconButton>
+        )}
         <IconButton
-          text={Locale.Export.Copy}
+          text={Locale.Export.Download}
           bordered
           shadow
-          icon={<CopyIcon />}
-          onClick={props.copy}
+          icon={<DownloadIcon />}
+          onClick={props.download}
         ></IconButton>
-      )}
-      <IconButton
-        text={Locale.Export.Download}
-        bordered
-        shadow
-        icon={<DownloadIcon />}
-        onClick={props.download}
-      ></IconButton>
-      <IconButton
-        text={Locale.Export.Share}
-        bordered
-        shadow
-        icon={<ShareIcon />}
-        onClick={() => showToast(Locale.WIP)}
-      ></IconButton>
-    </div>
+        <IconButton
+          text={Locale.Export.Share}
+          bordered
+          shadow
+          icon={loading ? <LoadingIcon /> : <ShareIcon />}
+          onClick={share}
+        ></IconButton>
+      </div>
+      <div
+        style={{
+          position: "fixed",
+          right: "200vw",
+          pointerEvents: "none",
+        }}
+      >
+        {shouldExport && (
+          <RenderExport
+            messages={props.messages ?? []}
+            onRender={onRenderMsgs}
+          />
+        )}
+      </div>
+    </>
   );
 }
 
@@ -323,7 +416,12 @@ export function ImagePreviewer(props: {
 
   return (
     <div className={styles["image-previewer"]}>
-      <PreviewActions copy={copy} download={download} showCopy={!isMobile} />
+      <PreviewActions
+        copy={copy}
+        download={download}
+        showCopy={!isMobile}
+        messages={props.messages}
+      />
       <div
         className={`${styles["preview-body"]} ${styles["default-theme"]}`}
         ref={previewRef}
@@ -417,7 +515,11 @@ export function MarkdownPreviewer(props: {
 
   return (
     <>
-      <PreviewActions copy={copy} download={download} />
+      <PreviewActions
+        copy={copy}
+        download={download}
+        messages={props.messages}
+      />
       <div className="markdown-body">
         <pre className={styles["export-content"]}>{mdText}</pre>
       </div>
diff --git a/app/components/message-selector.tsx b/app/components/message-selector.tsx
index 837591ac..300d4537 100644
--- a/app/components/message-selector.tsx
+++ b/app/components/message-selector.tsx
@@ -126,6 +126,8 @@ export function MessageSelector(props: {
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [startIndex, endIndex]);
 
+  const LATEST_COUNT = 4;
+
   return (
     <div className={styles["message-selector"]}>
       <div className={styles["message-filter"]}>
@@ -155,7 +157,7 @@ export function MessageSelector(props: {
               props.updateSelection((selection) => {
                 selection.clear();
                 messages
-                  .slice(messageCount - 10)
+                  .slice(messageCount - LATEST_COUNT)
                   .forEach((m) => selection.add(m.id!));
               })
             }
diff --git a/app/constant.ts b/app/constant.ts
index 577c0af6..0fb18c2f 100644
--- a/app/constant.ts
+++ b/app/constant.ts
@@ -42,3 +42,5 @@ export const ACCESS_CODE_PREFIX = "ak-";
 export const LAST_INPUT_KEY = "last-input";
 
 export const REQUEST_TIMEOUT_MS = 60000;
+
+export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown";
diff --git a/app/locales/cn.ts b/app/locales/cn.ts
index 989a54bf..48134e38 100644
--- a/app/locales/cn.ts
+++ b/app/locales/cn.ts
@@ -58,7 +58,7 @@ const cn = {
   Select: {
     Search: "搜索消息",
     All: "选取全部",
-    Latest: "最近十条",
+    Latest: "最近几条",
     Clear: "清除选中",
   },
   Memory: {
diff --git a/next.config.mjs b/next.config.mjs
index 9c0ce9fa..34c058b7 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -11,6 +11,10 @@ const nextConfig = {
         source: "/google-fonts/:path*",
         destination: "https://fonts.googleapis.com/:path*",
       },
+      {
+        source: "/sharegpt",
+        destination: "https://sharegpt.com/api/conversations",
+      },
     ];
 
     const apiUrl = process.env.API_URL;