diff --git a/src/components/connection/connection-detail.tsx b/src/components/connection/connection-detail.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..7ed786e2e24b1acef78f67f5a72f771359c600a7
--- /dev/null
+++ b/src/components/connection/connection-detail.tsx
@@ -0,0 +1,104 @@
+import dayjs from "dayjs";
+import { forwardRef, useImperativeHandle, useState } from "react";
+import { useLockFn } from "ahooks";
+import { Box, Button, Snackbar } from "@mui/material";
+import { deleteConnection } from "@/services/api";
+import { truncateStr } from "@/utils/truncate-str";
+import parseTraffic from "@/utils/parse-traffic";
+
+export interface ConnectionDetailRef {
+  open: (detail: IConnectionsItem) => void;
+}
+
+export const ConnectionDetail = forwardRef<ConnectionDetailRef>(
+  (props, ref) => {
+    const [open, setOpen] = useState(false);
+    const [detail, setDetail] = useState<IConnectionsItem>(null!);
+
+    useImperativeHandle(ref, () => ({
+      open: (detail: IConnectionsItem) => {
+        if (open) return;
+        setOpen(true);
+        setDetail(detail);
+      },
+    }));
+
+    const onClose = () => setOpen(false);
+
+    return (
+      <Snackbar
+        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
+        open={open}
+        onClose={onClose}
+        message={
+          detail ? (
+            <InnerConnectionDetail data={detail} onClose={onClose} />
+          ) : null
+        }
+      />
+    );
+  }
+);
+
+interface InnerProps {
+  data: IConnectionsItem;
+  onClose?: () => void;
+}
+
+const InnerConnectionDetail = ({ data, onClose }: InnerProps) => {
+  const { metadata, rulePayload } = data;
+  const chains = [...data.chains].reverse().join(" / ");
+  const rule = rulePayload ? `${data.rule}(${rulePayload})` : data.rule;
+  const host = metadata.host
+    ? `${metadata.host}:${metadata.destinationPort}`
+    : `${metadata.destinationIP}:${metadata.destinationPort}`;
+
+  const information = [
+    { label: "Host", value: host },
+    { label: "Download", value: parseTraffic(data.download).join(" ") },
+    { label: "Upload", value: parseTraffic(data.upload).join(" ") },
+    {
+      label: "DL Speed",
+      value: parseTraffic(data.curDownload ?? -1).join(" ") + "/s",
+    },
+    {
+      label: "UL Speed",
+      value: parseTraffic(data.curUpload ?? -1).join(" ") + "/s",
+    },
+    { label: "Chains", value: chains },
+    { label: "Rule", value: rule },
+    {
+      label: "Process",
+      value: truncateStr(metadata.process || metadata.processPath),
+    },
+    { label: "Time", value: dayjs(data.start).fromNow() },
+    { label: "Source", value: `${metadata.sourceIP}:${metadata.sourcePort}` },
+    { label: "Destination IP", value: metadata.destinationIP },
+    { label: "Type", value: `${metadata.type}(${metadata.network})` },
+  ];
+
+  const onDelete = useLockFn(async () => deleteConnection(data.id));
+
+  return (
+    <Box sx={{ userSelect: "text" }}>
+      {information.map((each) => (
+        <div key={each.label}>
+          <b>{each.label}</b>: <span>{each.value}</span>
+        </div>
+      ))}
+
+      <Box sx={{ textAlign: "right" }}>
+        <Button
+          variant="contained"
+          title="Close Connection"
+          onClick={() => {
+            onDelete();
+            onClose?.();
+          }}
+        >
+          Close
+        </Button>
+      </Box>
+    </Box>
+  );
+};
diff --git a/src/components/connection/connection-item.tsx b/src/components/connection/connection-item.tsx
index be2ea4844a6d223457d15e418a3f46957194156b..d04bf210e162a5dee3ebe1384b153d075a67d3a5 100644
--- a/src/components/connection/connection-item.tsx
+++ b/src/components/connection/connection-item.tsx
@@ -24,10 +24,11 @@ const Tag = styled("span")(({ theme }) => ({
 
 interface Props {
   value: IConnectionsItem;
+  onShowDetail?: () => void;
 }
 
-const ConnectionItem = (props: Props) => {
-  const { value } = props;
+export const ConnectionItem = (props: Props) => {
+  const { value, onShowDetail } = props;
 
   const { id, metadata, chains, start, curUpload, curDownload } = value;
 
@@ -44,8 +45,9 @@ const ConnectionItem = (props: Props) => {
       }
     >
       <ListItemText
-        sx={{ userSelect: "text" }}
+        sx={{ userSelect: "text", cursor: "pointer" }}
         primary={metadata.host || metadata.destinationIP}
+        onClick={onShowDetail}
         secondary={
           <Box sx={{ display: "flex", flexWrap: "wrap" }}>
             <Tag sx={{ textTransform: "uppercase", color: "success" }}>
@@ -71,5 +73,3 @@ const ConnectionItem = (props: Props) => {
     </ListItem>
   );
 };
-
-export default ConnectionItem;
diff --git a/src/components/connection/connection-table.tsx b/src/components/connection/connection-table.tsx
index 7d6696f581247c7c736900eadd02964b20d449c9..8f7b4b6c1a3ea8d96a99179e21a2b866240e70cf 100644
--- a/src/components/connection/connection-table.tsx
+++ b/src/components/connection/connection-table.tsx
@@ -1,37 +1,29 @@
 import dayjs from "dayjs";
 import { useMemo, useState } from "react";
 import { DataGrid, GridColDef } from "@mui/x-data-grid";
-import { Snackbar } from "@mui/material";
+import { truncateStr } from "@/utils/truncate-str";
 import parseTraffic from "@/utils/parse-traffic";
 
 interface Props {
   connections: IConnectionsItem[];
+  onShowDetail: (data: IConnectionsItem) => void;
 }
 
-const ConnectionTable = (props: Props) => {
-  const { connections } = props;
+export const ConnectionTable = (props: Props) => {
+  const { connections, onShowDetail } = props;
 
-  const [openedDetail, setOpenedDetail] = useState<IConnectionsItem | null>(
-    null
-  );
+  const [columnVisible, setColumnVisible] = useState<
+    Partial<Record<keyof IConnectionsItem, boolean>>
+  >({});
 
   const columns: GridColDef[] = [
-    {
-      field: "host",
-      headerName: "Host",
-      flex: 200,
-      minWidth: 200,
-      resizable: false,
-      disableColumnMenu: true,
-    },
+    { field: "host", headerName: "Host", flex: 220, minWidth: 220 },
     {
       field: "download",
       headerName: "Download",
       width: 88,
       align: "right",
       headerAlign: "right",
-      disableColumnMenu: true,
-      valueFormatter: (params: any) => parseTraffic(params.value).join(" "),
     },
     {
       field: "upload",
@@ -39,18 +31,13 @@ const ConnectionTable = (props: Props) => {
       width: 88,
       align: "right",
       headerAlign: "right",
-      disableColumnMenu: true,
-      valueFormatter: (params: any) => parseTraffic(params.value).join(" "),
     },
     {
       field: "dlSpeed",
       headerName: "DL Speed",
-      align: "right",
       width: 88,
+      align: "right",
       headerAlign: "right",
-      disableColumnMenu: true,
-      valueFormatter: (params: any) =>
-        parseTraffic(params.value).join(" ") + "/s",
     },
     {
       field: "ulSpeed",
@@ -58,55 +45,26 @@ const ConnectionTable = (props: Props) => {
       width: 88,
       align: "right",
       headerAlign: "right",
-      disableColumnMenu: true,
-      valueFormatter: (params: any) =>
-        parseTraffic(params.value).join(" ") + "/s",
-    },
-    {
-      field: "chains",
-      headerName: "Chains",
-      width: 360,
-      disableColumnMenu: true,
-    },
-    {
-      field: "rule",
-      headerName: "Rule",
-      width: 225,
-      disableColumnMenu: true,
-    },
-    {
-      field: "process",
-      headerName: "Process",
-      width: 480,
-      disableColumnMenu: true,
     },
+    { field: "chains", headerName: "Chains", flex: 360, minWidth: 360 },
+    { field: "rule", headerName: "Rule", flex: 300, minWidth: 250 },
+    { field: "process", headerName: "Process", flex: 480, minWidth: 480 },
     {
       field: "time",
       headerName: "Time",
-      width: 120,
+      flex: 120,
+      minWidth: 100,
       align: "right",
       headerAlign: "right",
-      disableColumnMenu: true,
-      valueFormatter: (params) => dayjs(params.value).fromNow(),
-    },
-    {
-      field: "source",
-      headerName: "Source",
-      width: 150,
-      disableColumnMenu: true,
     },
+    { field: "source", headerName: "Source", flex: 200, minWidth: 130 },
     {
       field: "destinationIP",
       headerName: "Destination IP",
-      width: 125,
-      disableColumnMenu: true,
-    },
-    {
-      field: "type",
-      headerName: "Type",
-      width: 160,
-      disableColumnMenu: true,
+      flex: 200,
+      minWidth: 130,
     },
+    { field: "type", headerName: "Type", flex: 160, minWidth: 100 },
   ];
 
   const connRows = useMemo(() => {
@@ -120,18 +78,16 @@ const ConnectionTable = (props: Props) => {
         host: metadata.host
           ? `${metadata.host}:${metadata.destinationPort}`
           : `${metadata.destinationIP}:${metadata.destinationPort}`,
-        download: each.download,
-        upload: each.upload,
-        dlSpeed: each.curDownload,
-        ulSpeed: each.curUpload,
+        download: parseTraffic(each.download).join(" "),
+        upload: parseTraffic(each.upload).join(" "),
+        dlSpeed: parseTraffic(each.curDownload).join(" ") + "/s",
+        ulSpeed: parseTraffic(each.curUpload).join(" ") + "/s",
         chains,
         rule,
-        process: truncateStr(
-          metadata.process || metadata.processPath || "",
-          16,
-          56
+        process: truncateStr(metadata.process || metadata.processPath)?.repeat(
+          10
         ),
-        time: each.start,
+        time: dayjs(each.start).fromNow(),
         source: `${metadata.sourceIP}:${metadata.sourcePort}`,
         destinationIP: metadata.destinationIP,
         type: `${metadata.type}(${metadata.network})`,
@@ -142,101 +98,15 @@ const ConnectionTable = (props: Props) => {
   }, [connections]);
 
   return (
-    <>
-      <DataGrid
-        rows={connRows}
-        columns={columns}
-        onRowClick={(e) => setOpenedDetail(e.row.connectionData)}
-        density="compact"
-        sx={{ border: "none", "div:focus": { outline: "none !important" } }}
-        hideFooter
-      />
-      <Snackbar
-        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
-        open={Boolean(openedDetail)}
-        onClose={() => setOpenedDetail(null)}
-        message={
-          openedDetail ? <SingleConnectionDetail data={openedDetail} /> : null
-        }
-      />
-    </>
-  );
-};
-
-export default ConnectionTable;
-
-const truncateStr = (str: string, prefixLen: number, maxLen: number) => {
-  if (str.length <= maxLen) return str;
-  return (
-    str.slice(0, prefixLen) + " ... " + str.slice(-(maxLen - prefixLen - 5))
-  );
-};
-
-const SingleConnectionDetail = ({ data }: { data: IConnectionsItem }) => {
-  const { metadata, rulePayload } = data;
-  const chains = [...data.chains].reverse().join(" / ");
-  const rule = rulePayload ? `${data.rule}(${rulePayload})` : data.rule;
-  const host = metadata.host
-    ? `${metadata.host}:${metadata.destinationPort}`
-    : `${metadata.destinationIP}:${metadata.destinationPort}`;
-
-  return (
-    <div>
-      <div>
-        {" "}
-        <b>Host</b>: <span>{host}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>Download</b>: <span>{parseTraffic(data.download).join(" ")}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>Upload</b>: <span>{parseTraffic(data.upload).join(" ")}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>DL Speed</b>:{" "}
-        <span>{parseTraffic(data.curDownload ?? -1).join(" ") + "/s"}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>UL Speed</b>:{" "}
-        <span>{parseTraffic(data.curUpload ?? -1).join(" ") + "/s"}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>Chains</b>: <span>{chains}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>Rule</b>: <span>{rule}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>Process</b>: <span>{metadata.process}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>ProcessPath</b>: <span>{metadata.processPath}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>Time</b>: <span>{dayjs(data.start).fromNow()}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>Source</b>:{" "}
-        <span>{`${metadata.sourceIP}:${metadata.sourcePort}`}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>Destination IP</b>: <span>{metadata.destinationIP}</span>{" "}
-      </div>
-      <div>
-        {" "}
-        <b>Type</b>: <span>{`${metadata.type}(${metadata.network})`}</span>{" "}
-      </div>
-    </div>
+    <DataGrid
+      hideFooter
+      rows={connRows}
+      columns={columns}
+      onRowClick={(e) => onShowDetail(e.row.connectionData)}
+      density="compact"
+      sx={{ border: "none", "div:focus": { outline: "none !important" } }}
+      columnVisibilityModel={columnVisible}
+      onColumnVisibilityModelChange={(e) => setColumnVisible(e)}
+    />
   );
 };
diff --git a/src/pages/connections.tsx b/src/pages/connections.tsx
index 9e2bce53b22c60466217f0decd6b17fe4c1fd29e..1b5f2e723df115d29a73f24c6abe529f791423cc 100644
--- a/src/pages/connections.tsx
+++ b/src/pages/connections.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useMemo, useState } from "react";
+import { useEffect, useMemo, useRef, useState } from "react";
 import { useLockFn } from "ahooks";
 import {
   Box,
@@ -18,8 +18,12 @@ import { atomConnectionSetting } from "@/services/states";
 import { useClashInfo } from "@/hooks/use-clash";
 import { BaseEmpty, BasePage } from "@/components/base";
 import { useWebsocket } from "@/hooks/use-websocket";
-import ConnectionItem from "@/components/connection/connection-item";
-import ConnectionTable from "@/components/connection/connection-table";
+import { ConnectionItem } from "@/components/connection/connection-item";
+import { ConnectionTable } from "@/components/connection/connection-table";
+import {
+  ConnectionDetail,
+  ConnectionDetailRef,
+} from "@/components/connection/connection-detail";
 
 const initConn = { uploadTotal: 0, downloadTotal: 0, connections: [] };
 
@@ -106,6 +110,8 @@ const ConnectionsPage = () => {
 
   const onCloseAll = useLockFn(closeAllConnections);
 
+  const detailRef = useRef<ConnectionDetailRef>(null!);
+
   return (
     <BasePage
       title={t("Connections")}
@@ -186,14 +192,24 @@ const ConnectionsPage = () => {
           {filterConn.length === 0 ? (
             <BaseEmpty text="No Connections" />
           ) : isTableLayout ? (
-            <ConnectionTable connections={filterConn} />
+            <ConnectionTable
+              connections={filterConn}
+              onShowDetail={(detail) => detailRef.current?.open(detail)}
+            />
           ) : (
             <Virtuoso
               data={filterConn}
-              itemContent={(index, item) => <ConnectionItem value={item} />}
+              itemContent={(index, item) => (
+                <ConnectionItem
+                  value={item}
+                  onShowDetail={() => detailRef.current?.open(item)}
+                />
+              )}
             />
           )}
         </Box>
+
+        <ConnectionDetail ref={detailRef} />
       </Paper>
     </BasePage>
   );
diff --git a/src/utils/parse-traffic.ts b/src/utils/parse-traffic.ts
index 49afb6e8ca2ca6ee44640be0823e48ad74e71435..514d24fe2dc2c56b0c0f764876090bd93ff82df6 100644
--- a/src/utils/parse-traffic.ts
+++ b/src/utils/parse-traffic.ts
@@ -1,6 +1,7 @@
 const UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
 
-const parseTraffic = (num: number) => {
+const parseTraffic = (num?: number) => {
+  if (typeof num !== "number") return ["NaN", ""];
   if (num < 1000) return [`${Math.round(num)}`, "B"];
   const exp = Math.min(Math.floor(Math.log2(num) / 10), UNITS.length - 1);
   const dat = num / Math.pow(1024, exp);
diff --git a/src/utils/truncate-str.ts b/src/utils/truncate-str.ts
new file mode 100644
index 0000000000000000000000000000000000000000..491fa07aefa74235efbb71b638462d57588e31d3
--- /dev/null
+++ b/src/utils/truncate-str.ts
@@ -0,0 +1,6 @@
+export const truncateStr = (str?: string, prefixLen = 16, maxLen = 56) => {
+  if (!str || str.length <= maxLen) return str;
+  return (
+    str.slice(0, prefixLen) + " ... " + str.slice(-(maxLen - prefixLen - 5))
+  );
+};