diff --git a/backend/routes/files.ts b/backend/routes/files.ts
index 6d48c78..25df8b7 100644
--- a/backend/routes/files.ts
+++ b/backend/routes/files.ts
@@ -13,6 +13,11 @@ const getFilesSchema = z
   .partial()
   .optional();
 
+const uploadSchema = z.object({
+  path: z.string().min(1),
+  size: z.string().min(1),
+});
+
 const filesDirList = process.env.FILE_DIRS
   ? process.env.FILE_DIRS.split(";").map((i) => ({
       name: i.split("/").at(-1),
@@ -68,6 +73,50 @@ const route = new Hono()
 
     return c.json([]);
   })
+  .post("/upload", async (c) => {
+    const input: any = (await c.req.parseBody()) as never;
+    const data = await uploadSchema.parseAsync(input);
+
+    const size = parseInt(input.size);
+    if (Number.isNaN(size) || !size) {
+      throw new HTTPException(400, { message: "Size is empty!" });
+    }
+
+    const files: File[] = [...Array(size)]
+      .map((_, idx) => input[`files.${idx}`])
+      .filter((i) => !!i);
+
+    if (!files.length) {
+      throw new HTTPException(400, { message: "Files is empty!" });
+    }
+
+    const pathSlices = data.path.split("/");
+    const baseName = pathSlices[1] || null;
+    const path = pathSlices.slice(2).join("/");
+    const baseDir = filesDirList.find((i) => i.name === baseName)?.path;
+    if (!baseDir?.length) {
+      throw new HTTPException(400, { message: "Path not found!" });
+    }
+
+    const targetDir = [baseDir, path].join("/");
+
+    // files.forEach((file) => {
+    //   const filepath = targetDir + "/" + file.name;
+    //   if (existsSync(filepath)) {
+    //     throw new HTTPException(400, { message: "File already exists!" });
+    //   }
+    // });
+
+    await Promise.all(
+      files.map(async (file) => {
+        const filepath = targetDir + "/" + file.name;
+        const buffer = await file.arrayBuffer();
+        await fs.writeFile(filepath, new Uint8Array(buffer));
+      })
+    );
+
+    return c.json({ success: true });
+  })
   .get("/download/*", async (c) => {
     const dlFile = c.req.query("dl") === "true";
     const url = new URL(c.req.url, `http://${c.req.header("host")}`);
@@ -132,4 +181,23 @@ const route = new Hono()
     }
   });
 
+function getFilePath(path: string) {
+  const pathSlices = path.split("/");
+  const baseName = pathSlices[1] || null;
+  const filePath = pathSlices.slice(2).join("/");
+
+  const baseDir = filesDirList.find((i) => i.name === baseName)?.path;
+  if (!baseDir?.length) {
+    throw new HTTPException(400, { message: "Path not found!" });
+  }
+
+  return {
+    path: [baseDir, filePath].join("/"),
+    pathname: ["", baseName, filePath].join("/"),
+    baseName,
+    baseDir,
+    filePath,
+  };
+}
+
 export default route;
diff --git a/src/app/apps/files/index.tsx b/src/app/apps/files/index.tsx
index 5c821b7..5ce94c1 100644
--- a/src/app/apps/files/index.tsx
+++ b/src/app/apps/files/index.tsx
@@ -1,14 +1,18 @@
-import FileList, { FileItem } from "@/components/pages/files/FileList";
+import FileList from "@/components/pages/files/FileList";
 import { useAsyncStorage } from "@/hooks/useAsyncStorage";
 import api from "@/lib/api";
 import { useAuth } from "@/stores/authStore";
 import BackButton from "@ui/BackButton";
-import Box from "@ui/Box";
 import Input from "@ui/Input";
 import { Stack } from "expo-router";
 import React from "react";
-import { useQuery } from "react-query";
+import { useMutation, useQuery } from "react-query";
 import { openFile } from "./utils";
+import FileDrop from "@/components/pages/files/FileDrop";
+import { showToast } from "@/stores/toastStore";
+import { HStack } from "@ui/Stack";
+import Button from "@ui/Button";
+import { Ionicons } from "@ui/Icons";
 
 const FilesPage = () => {
   const { isLoggedIn } = useAuth();
@@ -20,40 +24,72 @@ const FilesPage = () => {
       ? params.path.split("/").slice(0, -1).join("/")
       : null;
 
-  const { data } = useQuery({
+  const { data, refetch } = useQuery({
     queryKey: ["app/files", params],
     queryFn: () => api.files.$get({ query: params }).then((r) => r.json()),
     enabled: isLoggedIn,
   });
 
+  const upload = useMutation({
+    mutationFn: async (files: File[]) => {
+      const form: any = {
+        path: params.path,
+        size: files.length,
+      };
+
+      files.forEach((file, idx) => {
+        form[`files.${idx}`] = file;
+      });
+
+      const res = await api.files.upload.$post({ form });
+      return res.json();
+    },
+    onSuccess: () => {
+      showToast("Upload success!");
+      refetch();
+    },
+  });
+
+  const onFileDrop = (files: File[]) => {
+    if (!upload.isLoading) {
+      upload.mutate(files);
+    }
+  };
+
   return (
     <>
       <Stack.Screen
         options={{ headerLeft: () => <BackButton />, title: "Files" }}
       />
 
-      <Box className="px-2 py-2 bg-white">
+      <HStack className="px-2 py-2 bg-white gap-2">
+        <Button
+          icon={<Ionicons name="chevron-back" />}
+          disabled={parentPath == null}
+          className="px-3 border-gray-300"
+          labelClasses="text-gray-500"
+          variant="outline"
+          onPress={() => setParams({ ...params, path: parentPath })}
+        />
         <Input
           placeholder="/"
           value={params.path}
           onChangeText={(path) => setParams({ path })}
+          className="flex-1"
         />
-      </Box>
+      </HStack>
 
-      <FileList
-        files={data}
-        onSelect={(file) => {
-          if (file.path === "..") {
-            return setParams({ ...params, path: parentPath });
-          }
-          if (file.isDirectory) {
-            return setParams({ ...params, path: file.path });
-          }
-
-          openFile(file);
-        }}
-        canGoBack={parentPath != null}
-      />
+      <FileDrop onFileDrop={onFileDrop} isDisabled={upload.isLoading}>
+        <FileList
+          files={data}
+          onSelect={(file) => {
+            if (file.isDirectory) {
+              return setParams({ ...params, path: file.path });
+            }
+            openFile(file);
+          }}
+        />
+      </FileDrop>
     </>
   );
 };
diff --git a/src/app/apps/files/utils.ts b/src/app/apps/files/utils.ts
index 05ce887..cc98fea 100644
--- a/src/app/apps/files/utils.ts
+++ b/src/app/apps/files/utils.ts
@@ -1,6 +1,6 @@
-import { FileItem } from "@/components/pages/files/FileList";
 import { API_BASEURL } from "@/lib/constants";
 import authStore from "@/stores/authStore";
+import { FileItem } from "@/types/files";
 
 export function openFile(file: FileItem, dl = false) {
   const url = getFileUrl(file, dl);
diff --git a/src/components/pages/files/FileDrop.tsx b/src/components/pages/files/FileDrop.tsx
new file mode 100644
index 0000000..01554d9
--- /dev/null
+++ b/src/components/pages/files/FileDrop.tsx
@@ -0,0 +1,97 @@
+// @ts-ignore
+import { cn } from "@/lib/utils";
+import Box from "@ui/Box";
+import { Ionicons } from "@ui/Icons";
+import Text from "@ui/Text";
+import React, { useRef, useState } from "react";
+import { Platform } from "react-native";
+
+type Props = {
+  children: React.ReactNode;
+  isDisabled?: boolean;
+  onFileDrop?: (files: File[]) => void;
+  onDrop?: React.DragEventHandler<HTMLDivElement>;
+  onDragOver?: React.DragEventHandler<HTMLDivElement>;
+  onDragLeave?: React.DragEventHandler<HTMLDivElement>;
+  className?: string;
+};
+const isWeb = Platform.OS === "web";
+
+const FileDrop = ({ className, children, isDisabled, ...props }: Props) => {
+  const dragContainerRef = useRef<any>(null);
+  const overlayRef = useRef<any>(null);
+  const [isDragging, setDragging] = useState(false);
+
+  if (!isWeb) {
+    return children;
+  }
+
+  return (
+    <div
+      style={cn("flex-1 relative flex overflow-hidden")}
+      ref={dragContainerRef}
+      onDrop={(e) => {
+        e.preventDefault();
+        if (!isDragging || isDisabled) {
+          return;
+        }
+
+        setDragging(false);
+        props.onDrop && props.onDrop(e);
+
+        if (props.onFileDrop) {
+          const files = Array.from(e.dataTransfer.items)
+            .filter((i) => i.kind === "file")
+            .map((i) => i.getAsFile());
+          props.onFileDrop(files);
+        }
+      }}
+      onDragOver={(e) => {
+        e.preventDefault();
+        if (isDragging || isDisabled) {
+          return;
+        }
+
+        // ignore if not a file
+        if (
+          !e.dataTransfer.items ||
+          !e.dataTransfer.items.length ||
+          e.dataTransfer.items[0].kind !== "file"
+        ) {
+          return;
+        }
+
+        setDragging(true);
+        props.onDragOver && props.onDragOver(e);
+      }}
+      onDragLeave={(e) => {
+        e.preventDefault();
+        if (!isDragging || e.target !== overlayRef.current) {
+          return;
+        }
+
+        setDragging(false);
+        props.onDragLeave && props.onDragLeave(e);
+      }}
+    >
+      {children}
+
+      {isDragging && (
+        <Box
+          ref={overlayRef}
+          className="flex flex-col items-center justify-center absolute top-0 left-0 w-full h-full bg-black/10 z-10"
+        >
+          <Box
+            className="bg-white p-8 rounded-xl flex flex-col items-center gap-2"
+            style={{ pointerEvents: "none" }}
+          >
+            <Ionicons name="cloud-upload" style={{ fontSize: 48 }} />
+            <Text className="text-primary">Drop files here</Text>
+          </Box>
+        </Box>
+      )}
+    </div>
+  );
+};
+
+export default React.memo(FileDrop);
diff --git a/src/components/pages/files/FileList.tsx b/src/components/pages/files/FileList.tsx
index b1aca6c..7c01656 100644
--- a/src/components/pages/files/FileList.tsx
+++ b/src/components/pages/files/FileList.tsx
@@ -1,48 +1,54 @@
-import { FlatList, Pressable } from "react-native";
-import React, { useMemo } from "react";
+import { FlatList } from "react-native";
+import React from "react";
 import Text from "@ui/Text";
 import { HStack } from "@ui/Stack";
 import { cn } from "@/lib/utils";
 import { Ionicons } from "@ui/Icons";
-
-export type FileItem = {
-  name: string;
-  path: string;
-  isDirectory: boolean;
-};
+import Button from "@ui/Button";
+import Pressable from "@ui/Pressable";
+import { FileItem } from "@/types/files";
+import FileMenu, { openFileMenu } from "./FileMenu";
 
 type FileListProps = {
   files?: FileItem[];
   onSelect?: (file: FileItem) => void;
-  canGoBack?: boolean;
+  // onMenu?: (file: FileItem) => void;
+  onLongPress?: (file: FileItem) => void;
 };
 
-const FileList = ({ files, onSelect, canGoBack }: FileListProps) => {
-  const fileList = useMemo(() => {
-    if (canGoBack) {
-      return [{ name: "..", path: "..", isDirectory: true }, ...(files || [])];
-    }
-    return files || [];
-  }, [files, canGoBack]);
-
+const FileList = ({ files, onSelect, onLongPress }: FileListProps) => {
   return (
-    <FlatList
-      contentContainerStyle={cn("bg-white")}
-      data={fileList || []}
-      renderItem={({ item }) => (
-        <FileItem file={item} onPress={() => onSelect?.(item)} />
-      )}
-      keyExtractor={(item) => item.path}
-    />
+    <>
+      <FlatList
+        style={cn("flex-1")}
+        contentContainerStyle={cn("bg-white")}
+        data={files || []}
+        renderItem={({ item }) => (
+          <FileItemList
+            file={item}
+            onPress={() => onSelect?.(item)}
+            onLongPress={() => onLongPress?.(item)}
+            onMenuPress={() => openFileMenu(item)}
+          />
+        )}
+        keyExtractor={(item) => item.path}
+      />
+
+      <FileMenu />
+    </>
   );
 };
 
-const FileItem = ({
+const FileItemList = ({
   file,
   onPress,
+  onLongPress,
+  onMenuPress,
 }: {
   file: FileItem;
   onPress?: () => void;
+  onLongPress?: () => void;
+  onMenuPress?: () => void;
 }) => {
   return (
     <HStack className="bg-white border-b border-gray-200 items-center">
@@ -54,6 +60,13 @@ const FileItem = ({
           )
         }
         onPress={onPress}
+        onLongPress={onLongPress}
+        onContextMenu={(e) => {
+          if (onMenuPress) {
+            e.preventDefault();
+            onMenuPress();
+          }
+        }}
       >
         <Ionicons
           name={file.isDirectory ? "folder" : "document"}
@@ -64,6 +77,12 @@ const FileItem = ({
         />
         <Text numberOfLines={1}>{file.name}</Text>
       </Pressable>
+      <Button
+        icon={<Ionicons name="ellipsis-vertical" />}
+        variant="ghost"
+        className="h-full px-4"
+        onPress={onMenuPress}
+      />
     </HStack>
   );
 };
diff --git a/src/components/pages/files/FileMenu.tsx b/src/components/pages/files/FileMenu.tsx
new file mode 100644
index 0000000..95b66b8
--- /dev/null
+++ b/src/components/pages/files/FileMenu.tsx
@@ -0,0 +1,52 @@
+import React from "react";
+import { createStore, useStore } from "zustand";
+import { FileItem } from "@/types/files";
+import Text from "@ui/Text";
+import List from "@ui/List";
+import { Ionicons } from "@ui/Icons";
+import { cn } from "@/lib/utils";
+import ActionSheet from "@ui/ActionSheet";
+import { HStack } from "@ui/Stack";
+import Button from "@ui/Button";
+
+type Store = {
+  isVisible: boolean;
+  file?: FileItem | null;
+};
+
+const store = createStore<Store>(() => ({
+  isVisible: false,
+  file: null,
+}));
+
+export const openFileMenu = (file: FileItem) => {
+  store.setState({ isVisible: true, file });
+};
+
+const FileMenu = () => {
+  const { isVisible, file } = useStore(store);
+  const onClose = () => store.setState({ isVisible: false });
+
+  return (
+    <ActionSheet isVisible={isVisible} onClose={onClose}>
+      <Text className="text-lg md:text-xl" numberOfLines={1}>
+        {file?.name}
+      </Text>
+
+      <List className="mt-4">
+        <List.Item icon={<Ionicons name="pencil" />}>Rename</List.Item>
+        <List.Item icon={<Ionicons name="copy" />}>Copy</List.Item>
+        <List.Item icon={<Ionicons name="cut-outline" />}>Move</List.Item>
+        <List.Item icon={<Ionicons name="trash" />}>Delete</List.Item>
+      </List>
+
+      <HStack className="justify-end mt-6">
+        <Button variant="ghost" onPress={onClose}>
+          Cancel
+        </Button>
+      </HStack>
+    </ActionSheet>
+  );
+};
+
+export default FileMenu;
diff --git a/src/components/ui/ActionSheet.tsx b/src/components/ui/ActionSheet.tsx
new file mode 100644
index 0000000..edb2e5b
--- /dev/null
+++ b/src/components/ui/ActionSheet.tsx
@@ -0,0 +1,26 @@
+import React, { ComponentProps } from "react";
+import Modal from "react-native-modal";
+import { cn } from "@/lib/utils";
+import Container from "./Container";
+
+type ActionSheetProps = Partial<ComponentProps<typeof Modal>> & {
+  onClose?: () => void;
+};
+
+const ActionSheet = ({ onClose, children, ...props }: ActionSheetProps) => {
+  return (
+    <Modal
+      style={cn("justify-end md:justify-center m-0 md:m-4")}
+      onBackButtonPress={onClose}
+      onBackdropPress={onClose}
+      backdropOpacity={0.3}
+      {...props}
+    >
+      <Container className="bg-white p-4 md:p-8 rounded-t-xl md:rounded-xl">
+        {children}
+      </Container>
+    </Modal>
+  );
+};
+
+export default ActionSheet;
diff --git a/src/components/ui/Box.tsx b/src/components/ui/Box.tsx
index 5524e56..d254c83 100644
--- a/src/components/ui/Box.tsx
+++ b/src/components/ui/Box.tsx
@@ -1,13 +1,18 @@
 import { cn } from "@/lib/utils";
 import { ComponentPropsWithClassName } from "@/types/components";
+import { forwardRef } from "react";
 import { View } from "react-native";
 
 type Props = ComponentPropsWithClassName<typeof View>;
 
-const Box = ({ className, style, ...props }: Props) => {
+const Box = forwardRef(({ className, style, ...props }: Props, ref: any) => {
   return (
-    <View style={{ ...cn(className), ...((style || {}) as any) }} {...props} />
+    <View
+      ref={ref}
+      style={{ ...cn(className), ...((style || {}) as any) }}
+      {...props}
+    />
   );
-};
+});
 
 export default Box;
diff --git a/src/components/ui/List.tsx b/src/components/ui/List.tsx
new file mode 100644
index 0000000..cd65c9b
--- /dev/null
+++ b/src/components/ui/List.tsx
@@ -0,0 +1,38 @@
+import React from "react";
+import { HStack, VStack } from "./Stack";
+import { cn } from "@/lib/utils";
+import Pressable from "./Pressable";
+import Text from "./Text";
+import Slot from "./Slot";
+
+type Props = {
+  className?: any;
+  children: React.ReactNode;
+};
+
+const List = ({ className, children }: Props) => {
+  return <VStack className={cn(className)}>{children}</VStack>;
+};
+
+type ListItemProps = {
+  className?: any;
+  children: React.ReactNode;
+  icon?: React.ReactNode;
+};
+
+const ListItem = ({ className, icon, children }: ListItemProps) => {
+  return (
+    <Pressable style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1 })}>
+      <HStack className={cn("py-2 border-b border-gray-200", className)}>
+        {icon ? (
+          <Slot.Text style={cn("text-gray-800 text-xl w-8")}>{icon}</Slot.Text>
+        ) : null}
+
+        <Text>{children}</Text>
+      </HStack>
+    </Pressable>
+  );
+};
+List.Item = ListItem;
+
+export default List;
diff --git a/src/components/ui/Pressable.tsx b/src/components/ui/Pressable.tsx
new file mode 100644
index 0000000..a599aaa
--- /dev/null
+++ b/src/components/ui/Pressable.tsx
@@ -0,0 +1,12 @@
+import { ComponentProps, forwardRef } from "react";
+import { Pressable as BasePressable } from "react-native";
+
+type Props = ComponentProps<typeof BasePressable> & {
+  onContextMenu?: (event: PointerEvent) => void;
+};
+
+const Pressable = forwardRef((props: Props, ref: any) => {
+  return <BasePressable ref={ref} {...props} />;
+});
+
+export default Pressable;
diff --git a/src/types/files.ts b/src/types/files.ts
new file mode 100644
index 0000000..fbdeb23
--- /dev/null
+++ b/src/types/files.ts
@@ -0,0 +1,5 @@
+export type FileItem = {
+  name: string;
+  path: string;
+  isDirectory: boolean;
+};