files

ui

File tree display with collapsible folders. Uses Base UI Collapsible for animations.

fumadocs/filesv0.1.0react, next

Preview

sidebar.tsx
header.tsx
footer.tsx
index.ts
package.json
tsconfig.json

Installation

$npx @document0/cli add fumadocs/files

This will also install: @base-ui/react@>=1.0.0, lucide-react@>=0.300.0, tailwind-merge@>=2.0.0

Usage

import { Files } from "./components/fumadocs/files";

// Example usage in your layout or page:
<Files />

Source

After installation, this lives at components/fumadocs/files/Files.tsx and you can modify it however you like.

"use client";

import { Collapsible } from "@base-ui/react/collapsible";
import { FileIcon, FolderIcon, FolderOpen } from "lucide-react";
import { type HTMLAttributes, type ReactNode, useState } from "react";
import { twMerge } from "tailwind-merge";

/**
 * File tree display component with collapsible folders.
 *
 * Ported from fumadocs-ui (Base UI variant).
 *
 * Usage:
 * ```tsx
 * <Files>
 *   <Folder name="src" defaultOpen>
 *     <File name="index.ts" />
 *     <File name="utils.ts" />
 *   </Folder>
 *   <File name="package.json" />
 * </Files>
 * ```
 */
export function Files({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
  return (
    <div
      className={twMerge(
        "not-prose rounded-lg border bg-card p-2 text-sm",
        className,
      )}
    >
      {props.children}
    </div>
  );
}

export interface FileProps extends HTMLAttributes<HTMLDivElement> {
  name: string;
  icon?: ReactNode;
}

export interface FolderProps extends HTMLAttributes<HTMLDivElement> {
  name: string;
  disabled?: boolean;
  defaultOpen?: boolean;
}

export function File({
  name,
  icon = <FileIcon className="size-4" />,
  className,
  ...rest
}: FileProps) {
  return (
    <div
      className={twMerge(
        "flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground [&_svg]:size-4",
        className,
      )}
      {...rest}
    >
      {icon}
      {name}
    </div>
  );
}

export function Folder({ name, defaultOpen = false, children, ...props }: FolderProps) {
  const [open, setOpen] = useState(defaultOpen);

  return (
    <Collapsible.Root open={open} onOpenChange={setOpen}>
      <Collapsible.Trigger
        className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground [&_svg]:size-4"
      >
        {open ? <FolderOpen className="size-4" /> : <FolderIcon className="size-4" />}
        {name}
      </Collapsible.Trigger>
      <Collapsible.Panel className="h-[var(--collapsible-panel-height)] overflow-hidden transition-[height] duration-200 ease-out data-[ending-style]:h-0 data-[starting-style]:h-0">
        <div className="ms-2 flex flex-col border-l ps-2">{children}</div>
      </Collapsible.Panel>
    </Collapsible.Root>
  );
}

Tags

filestreemdxreact