diff --git a/package.json b/package.json
index 2ddf6be7c07768145ba11d65141e940875e50a4e..1a934a48d553c0a315205dea0bb4a51c86a3fdd2 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
     "react": "^17.0.0",
     "react-dom": "^17.0.0",
     "react-router-dom": "^6.0.2",
+    "react-virtuoso": "^2.3.1",
     "recoil": "^0.5.2"
   },
   "devDependencies": {
diff --git a/src/components/proxy-group.tsx b/src/components/proxy-group.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6be59f4873e2664f16a8039f097888166efb9365
--- /dev/null
+++ b/src/components/proxy-group.tsx
@@ -0,0 +1,142 @@
+import { useState } from "react";
+import { Virtuoso } from "react-virtuoso";
+import {
+  Box,
+  Collapse,
+  Divider,
+  IconButton,
+  List,
+  ListItem,
+  ListItemButton,
+  ListItemIcon,
+  ListItemText,
+} from "@mui/material";
+import {
+  SendRounded,
+  ExpandLessRounded,
+  ExpandMoreRounded,
+  MyLocationRounded,
+  NetworkCheckRounded,
+  CheckCircleOutlineRounded,
+} from "@mui/icons-material";
+import services from "../services";
+import type { ProxyItem, ProxyGroupItem } from "../services/proxy";
+
+interface ItemProps {
+  proxy: ProxyItem;
+  selected: boolean;
+  onClick?: (name: string) => void;
+}
+
+const Item = ({ proxy, selected, onClick }: ItemProps) => {
+  return (
+    <ListItem sx={{ py: 0, pl: 4 }}>
+      <ListItemButton
+        selected={selected}
+        onClick={() => onClick?.(proxy.name)}
+        sx={{ borderRadius: 1, py: 0.5 }}
+      >
+        <ListItemText title={proxy.name} secondary={proxy.name} />
+        <ListItemIcon
+          sx={{ justifyContent: "flex-end", color: "primary.main" }}
+        >
+          {selected && <CheckCircleOutlineRounded sx={{ fontSize: 16 }} />}
+        </ListItemIcon>
+      </ListItemButton>
+    </ListItem>
+  );
+};
+
+interface Props {
+  group: ProxyGroupItem;
+}
+
+const ProxyGroup = ({ group }: Props) => {
+  const [open, setOpen] = useState(false);
+  const [now, setNow] = useState(group.now);
+
+  const proxies = group.all ?? [];
+
+  const onUpdate = async (name: string) => {
+    // can not call update
+    if (group.type !== "Selector") {
+      // Todo
+      // error Tips
+      return;
+    }
+    const oldValue = now;
+    try {
+      setNow(name);
+      await services.updateProxy(group.name, name);
+    } catch {
+      setNow(oldValue);
+      // Todo
+      // error tips
+    }
+  };
+
+  return (
+    <>
+      <ListItem button onClick={() => setOpen(!open)}>
+        <ListItemText
+          primary={group.name}
+          secondary={
+            <>
+              <SendRounded color="primary" sx={{ mr: 1, fontSize: 14 }} />
+              <span>{now}</span>
+            </>
+          }
+          secondaryTypographyProps={{
+            sx: { display: "flex", alignItems: "center" },
+          }}
+        />
+
+        {open ? <ExpandLessRounded /> : <ExpandMoreRounded />}
+      </ListItem>
+
+      <Collapse in={open} timeout="auto" unmountOnExit>
+        <Box sx={{ pl: 4, pr: 3, my: 0.5 }}>
+          <IconButton size="small" title="location">
+            <MyLocationRounded />
+          </IconButton>
+          <IconButton size="small" title="check">
+            <NetworkCheckRounded />
+          </IconButton>
+        </Box>
+
+        {proxies.length >= 10 ? (
+          <Virtuoso
+            style={{ height: "400px", marginBottom: "4px" }}
+            totalCount={proxies.length}
+            itemContent={(index) => (
+              <Item
+                proxy={proxies[index]}
+                selected={proxies[index].name === now}
+                onClick={onUpdate}
+              />
+            )}
+          />
+        ) : (
+          <List
+            component="div"
+            disablePadding
+            sx={{ maxHeight: "400px", overflow: "auto", mb: "4px" }}
+          >
+            {proxies.map((proxy) => (
+              <Item
+                key={proxy.name}
+                proxy={proxy}
+                selected={proxy.name === now}
+                onClick={onUpdate}
+              />
+            ))}
+          </List>
+        )}
+
+        <Divider variant="middle" />
+      </Collapse>
+    </>
+  );
+};
+
+export default ProxyGroup;
diff --git a/src/pages/proxy.tsx b/src/pages/proxy.tsx
index 2808614d425710f089180c4022f4c5d9f5585628..2bbcbcc051dc4da3a3829704e846ebb32d5a442e 100644
--- a/src/pages/proxy.tsx
+++ b/src/pages/proxy.tsx
@@ -1,5 +1,33 @@
+import { useEffect, useState } from "react";
+import { Box, List, Typography } from "@mui/material";
+import services from "../services";
+import ProxyGroup from "../components/proxy-group";
+import type { ProxyGroupItem } from "../services/proxy";
+
 const ProxyPage = () => {
-  return <h1>Proxy</h1>;
+  const [groups, setGroups] = useState<ProxyGroupItem[]>([]);
+
+  useEffect(() => {
+    // Todo
+    // result cache
+    services.getProxyInfo().then((res) => {
+      setGroups(res.groups);
+    });
+  }, []);
+
+  return (
+    <Box sx={{ width: 0.9, maxWidth: "850px", mx: "auto", mb: 2 }}>
+      <Typography variant="h4" component="h1" sx={{ py: 2 }}>
+        Proxy Groups
+      </Typography>
+
+      <List sx={{ borderRadius: 1, boxShadow: 2 }}>
+        {groups.map((group) => (
+          <ProxyGroup key={group.name} group={group} />
+        ))}
+      </List>
+    </Box>
+  );
 };
 
 export default ProxyPage;
diff --git a/src/services/index.ts b/src/services/index.ts
index a15cef0bde17916d2e0a58df508065943f506375..6832043ac008cbb3f4e8b0089939f31dc03f9c89 100644
--- a/src/services/index.ts
+++ b/src/services/index.ts
@@ -1,5 +1,7 @@
+import * as proxy from "./proxy";
 import * as traffic from "./traffic";
 
 export default {
+  ...proxy,
   ...traffic,
 };
diff --git a/src/services/proxy.ts b/src/services/proxy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..452926ea8ae499a22c2c6d188c686b68dd54ba23
--- /dev/null
+++ b/src/services/proxy.ts
@@ -0,0 +1,47 @@
+import axiosIns from "./base";
+
+export interface ProxyItem {
+  name: string;
+  type: string;
+  udp: boolean;
+  history: {
+    time: string;
+    delay: number;
+  }[];
+  all?: string[];
+  now?: string;
+}
+
+export type ProxyGroupItem = Omit<ProxyItem, "all" | "now"> & {
+  all?: ProxyItem[];
+  now?: string;
+};
+
+/// Get the Proxy infomation
+export async function getProxyInfo() {
+  const response = (await axiosIns.get("/proxies")) as any;
+  const results = (response?.proxies ?? {}) as Record<string, ProxyItem>;
+
+  const global = results["GLOBAL"] || results["global"];
+  const proxies = Object.values(results).filter((each) => each.all == null);
+
+  const groups = Object.values(results).filter(
+    (each) => each.name.toLocaleUpperCase() !== "GLOBAL" && each.all != null
+  ) as ProxyGroupItem[];
+
+  groups.forEach((each) => {
+    // @ts-ignore
+    each.all = each.all?.map((item) => results[item]).filter((e) => e);
+  });
+
+  return {
+    global,
+    groups,
+    proxies,
+  };
+}
+
+/// Update the Proxy Choose
+export async function updateProxy(group: string, proxy: string) {
+  return axiosIns.put(`/proxies/${group}`, { name: proxy });
+}