Let's create a File Manager From Scratch With React And Typescript Chapter IV: Creating FileManager Component
Tags: react, typescript, mongez
Published: 2022-09-12 21:55:41

---
title: Let's create a File Manager From Scratch With React And Typescript Chapter IV: Creating FileManager Component 
published: true
description: 
series: File Manager React Mongez
tags: react, typescript, mongez
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/314szylzr6lwrc0bizyb.png
# Use a ratio of 100:42 for best results.
---

Let's head now to the part where i don't really admire, working with UI, let's create our `FileManager` component, but firstly let's define what props it accepts.

> For encapsulation behavior, i'll add the FileManager types file inside the component directory.

Create `file-manager/components/FileManager` directory then create `FileManager.types.ts` inside it

```tsx
// file-manager/components/FileManager/FileManager.types.ts
import { Node } from "../../types/FileManager.types";

export type FileManagerProps = {
  /**
   * Open State for the file manager
   */
  open: boolean;
  /**
   * Callback for when the file manager is closed
   */
  onClose: () => void;
  /**
   * Root path to open in the file manager
   *
   * @default "/"
   */
  rootPath?: string;
  /**
   * Callback for when a file/directory is selected
   */
  onSelect?: (node: Node) => void;
  /**
   * Callback for when a file/directory is double clicked
   */
  onDoubleClick?: (node: Node) => void;
  /**
   * Callback for when a file/directory is right clicked
   */
  onRightClick?: (node: Node) => void;
  /**
   * Callback for when a file/directory is copied
   */
  onCopy?: (node: Node) => void;
  /**
   * Callback for when a file/directory is cut
   */
  onCut?: (node: Node) => void;
  /**
   * Callback for when a file/directory is pasted
   * The old node will contain the old path 
   * and the new node will contain the new path
   */
  onPaste?: (node: Node, oldNode: Node) => void;
  /**
   * Callback for when a file/directory is deleted
   */
  onDelete?: (node: Node) => void;
  /**
   * Callback for when a file/directory is renamed
   * The old node will contain the old path/name 
   * and the new node will contain the new path/name
   */
  onRename?: (node: Node, oldNode: Node) => void;
  /**
   * Callback for when a directory is created
   */
  onCreateDirectory?: (directory: Node) => void;
  /**
   * Callback for when file(s) is uploaded
   */
  onUpload?: (files: Node[]) => void;
  /**
   * Callback for when a file is downloaded
   */
  onDownload?: (node: Node) => void;
};
```

**wooohoo**, don't freak out, i'm just trying to make you imagine the big picture, try to read the component props slowly and carefully and the comments above each prop.

For the time being we'll just need only two props, `open` and `onClose` which are the only required props.

```tsx
// file-manager/components/FileManager/FileManager.types.ts
export type FileManagerProps = {
  /**
   * Open State for the file manager
   */
  open: boolean;
  /**
   * Root path to open in the file manager
   *
   * @default "/"
   */
  rootPath?: string;
  /**
   * Callback for when the file manager is closed
   */
  onClose: () => void;
};
```

These two props are responsible for opening and closing the file manager, as we will open it up in a modal.

Now let's create our `FileManager` Component.

```tsx
// file-manager/components/FileManager/FileManager.ts
import { FileManagerProps } from "./FileManager.types";

export default function FileManager({ open, onClose }: FileManagerProps) {
  return <div>FileManager</div>;
}
```

Before we proceed to the next step, we'll create an `index` file to encapsulate the files imports from it directly.

```ts
// file-manager/components/FileManager/index.ts
export { default } from "./FileManager";
export * from "./FileManager.types";
```

Now let's go to our `HomePage` and call our newly created component, the `FileManager`.

> **Small modification**, we'll also create a component file `HomePage` for the home page rather than sitting it in the index file, then we'll export it from the index as we did in the file manager.

```tsx
// home/components/HomePage/HomePage.tsx
import Helmet from "@mongez/react-helmet";
import FileManager from "app/file-manager/components/FileManager";
import { useState } from "react";

export default function HomePage() {
  const [openFileManager, setOpenFileManager] = useState(false);

  return (
    <>
      <Helmet title="home" appendAppName={false} />
      <h1>Welcome To Home Page</h1>
      <FileManager
        open={openFileManager}
        onClose={() => setOpenFileManager(false)}
      />
    </>
  );
}
```

We created a state to manage the open/close state for the file manager we set it to `false` as default, then we called our component in the function return.

Now let's see what will be in the browser, you should have something like this:

![Home Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zhj11t36ty6w32vsv9ls.png)

## Setting up Mantine Styles

I won't stop here too long, we'll just do what the docs says and import [Mantine Theme Provider](https://mantine.dev/pages/basics/) in top of our application, which can be set in the `Root` component.

Let's go to `src/apps/front-office/design-system/layouts/Root.tsx` and update it with the following:

```tsx
import { AppShell, MantineProvider } from "@mantine/core";
import { BasicComponentProps } from "../../utils/types";

/**
 * The root should be used with react-router's configuration for rootComponent.
 * So it will wrap the entire app.
 * It can be useful for single operations as this component will only render once in the entire application life cycle.
 * You may for instance fetch settings from the server before loading the app or a Bearer token to work with the API.
 */
export default function Root({ children }: BasicComponentProps) {
  return (
    <>
      <MantineProvider withGlobalStyles withNormalizeCSS>
        <AppShell>{children}</AppShell>
      </MantineProvider>
    </>
  );
}
```

All what we did is we added the theme provider to be wrapping our children, which is literally the entire application.

> The AppShell Component will just make some paddings around the content, you can skip it if you like.

Now you should see something like this:

![Home Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00qvyqk3r4gnmdv65u6w.png)

### Bonus Tip

You can change the color scheme of Mantine by setting `colorScheme` to `dark`.

```tsx
import { AppShell, MantineProvider } from "@mantine/core";
import { BasicComponentProps } from "../../utils/types";

/**
 * The root should be used with react-router's configuration for rootComponent.
 * So it will wrap the entire app.
 * It can be useful for single operations as this component will only render once in the entire application life cycle.
 * You may for instance fetch settings from the server before loading the app or a Bearer token to work with the API.
 */
export default function Root({ children }: BasicComponentProps) {
  return (
    <>
      <MantineProvider
        theme={{
          colorScheme: "dark",
        }}
        withGlobalStyles
        withNormalizeCSS>
        <AppShell>{children}</AppShell>
      </MantineProvider>
    </>
  );
}
```

Now it will look like this:

![Dark Theme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dk142hxc7yjxjgf9imy4.png)

You can smartly detect if the user (like me) prefers dark mode and is setting it in his/her device using `userPrefersDarkMode` utility from [Mongez Dom](https://github.com/hassanzohdy/mongez-dom#detects-if-users-device-is-in-dark-mode).

```tsx
import { AppShell, MantineProvider } from "@mantine/core";
import { userPrefersDarkMode } from "@mongez/dom";
import { BasicComponentProps } from "../../utils/types";

/**
 * The root should be used with react-router's configuration for rootComponent.
 * So it will wrap the entire app.
 * It can be useful for single operations as this component will only render once in the entire application life cycle.
 * You may for instance fetch settings from the server before loading the app or a Bearer token to work with the API.
 */
export default function Root({ children }: BasicComponentProps) {
  return (
    <>
      <MantineProvider
        theme={{
          colorScheme: userPrefersDarkMode() ? "dark" : "light",
        }}
        withGlobalStyles
        withNormalizeCSS>
        <AppShell>{children}</AppShell>
      </MantineProvider>
    </>
  );
}
```

Now it will appear based on the user's device default theme mode.

> For the demo purpose, we'll stick to the light mode for now, maybe we change it later.

Heading back to our file manager component.

## File Manager In Modal

Now we'll add wrap our file manager in a [Modal](https://mantine.dev/core/modal/) so we can open and close it from the parent component.

```tsx
// FileManager.tsx
import { Modal } from "@mantine/core";
import { FileManagerProps } from "./FileManager.types";

export default function FileManager({ open, onClose }: FileManagerProps) {
  return (
    <>
      <Modal size="xl" opened={open} onClose={onClose}>
        <h1>File Manager</h1>
      </Modal>
    </>
  );
}
```

I just added the modal around simple content which pops with the `File Manager` keywords and set the size to be `xl`.

Now let's add a button in the home page to open the file manager.

```tsx
// HomePage.tsx
import { Button } from "@mantine/core";
import Helmet from "@mongez/react-helmet";
import FileManager from "app/file-manager/components/FileManager";
import { useState } from "react";

export default function HomePage() {
  const [openFileManager, setOpenFileManager] = useState(false);

  return (
    <>
      <Helmet title="home" appendAppName={false} />
      <h1>Welcome To Home Page</h1>
      <Button
        onClick={() => setOpenFileManager(true)}
        variant="gradient"
        gradient={{ from: "red", to: "orange" }}>
        Open File Manager
      </Button>
      <FileManager
        open={openFileManager}
        onClose={() => setOpenFileManager(false)}
      />
    </>
  );
}
```

I used a nice [Gradient Button](https://mantine.dev/core/button/#gradient-variant) to open the file manager.

Now you should see this in your browser

![Home Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ptlekwsnmvkuhvdxg4ku.png)

And when you click on the button, you should see this

![File Manager Modal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9k3e4whm9g5md0ubftur.png)

We are now ready to establish our file manager skelton, which will be in our next tutorial.

## Article Repository

You can see chapter files in [Github Repository](https://github.com/hassanzohdy/file-manager-react/tree/3-file-manager-component)

> Don't forget the `main` branch has the latest updated code.

Salam.