Logo

File Select

A drag-and-drop file upload component with customizable styling and file constraints.

Note

Currently, this component just provides a structured UI. Logic is required to be handled by you.

Installation

Run the following command to add the function to your project:

npx shadcn@latest add @flowui/file-select

Install Dependencies

Ensure you have lucide-react installed for the icons.

npm install lucide-react

Create File Select Component

Create the component file-select.tsx and copy the following code:

components/ui/file-select.tsx
"use client";

import { cn } from "@/lib/utils";
import { Upload } from "lucide-react";
import { useId, useRef } from "react";

type SlotProps = {
    children?: React.ReactNode;
    className?: string;
};

type FileSelectProps = {
    accept?: string;
    multiple?: boolean;
} & SlotProps;

type FileSelectTextProps = SlotProps;

export const FileSelectLogo = ({ children, className } : SlotProps) => {
    return children ?? <Upload className={cn(className)} />
}

export const FileSelectText = ({ children, className } : FileSelectTextProps) => {
    return (
        <div className={cn(
            "text-sm text-center",
            className
            )}
        >
            {children}
        </div>
    )
}

export const FileSelect = ({
    className,
    children,
    accept,
    multiple
} : FileSelectProps) => {
    const inputId = useId();
    const inputRef = useRef<HTMLInputElement>(null);
    
    const handleKeyDown = (e: React.KeyboardEvent<HTMLLabelElement>) => {
        if(e.key === "Enter" || e.key === " ") {
            e.preventDefault();
            inputRef.current?.click();
        }
    }
    return (
        <>
            <input
                id={inputId}
                ref={inputRef}
                type="file"
                className="hidden"
                accept={accept}
                multiple={multiple}
            />
            <label
                tabIndex={0}
                role="button"
                onKeyDown={handleKeyDown}
                htmlFor={inputId}
                className={cn(
                    "flex flex-col justify-center items-center size-50 border border-dashed rounded-lg cursor-pointer hover:bg-accent hover:text-accent-foreground transition-colors",
                    className
                )}
            >
                {children}
            </label>
        </>
    )
}

Usage

Basic Structure

The FileSelect component is built with composability in mind. It consists of three main parts:

  • <FileSelect>: The main container that handles the file input logic.
  • <FileSelectLogo>: The icon or visual indicator (drag handle).
  • <FileSelectText>: The helper text instructions.

Here is a basic skeleton of how to use it:

<FileSelect>
    <FileSelectLogo />
    <FileSelectText>
        Drop files here
    </FileSelectText>
</FileSelect>

Profile Upload (Compact)

Use the component for profile picture uploads or other compact scenarios.

import { FileSelect } from "@/components/ui/file-select";
import { User, Upload } from "lucide-react";

export const FileSelectProfile = () => {
    return (
        <FileSelect className="size-10 rounded-full">
            <FileSelectLogo className="size-4" />
        </FileSelect>
    )
}

Button Style

Sometimes you just want a simple button.

import { FileSelect } from "@/components/ui/file-select";
import { Upload } from "lucide-react";

export const FileSelectCompact = () => {
    return (
        <FileSelect className="h-12 w-full max-w-xs mx-auto flex flex-row items-center justify-center gap-3 rounded-full border border-input bg-background px-4 py-2 hover:bg-accent hover:text-accent-foreground cursor-pointer transition-colors shadow-sm">
            <FileSelectLogo className="size-4" />
            <FileSelectText>
                Upload Document
            </FileSelectText>
        </FileSelect>
    )
}

Configuration

Multiple Files

Set the multiple prop to allow selecting more than one file.

<FileSelect multiple className="...">
    ...
</FileSelect>

Accepted File Types

Use the accept prop to restrict the selectable file types (e.g., images only).

<FileSelect accept="image/png, image/jpeg" className="...">
    ...
</FileSelect>

Props

FileSelect

PropTypeDefaultDescription
childrenReact.ReactNode-Content to be rendered inside the drop area.
classNamestring-Additional class names to apply to the container.
acceptstring-A string that defines the file types the file input should accept.
multiplebooleanfalseWhen true, allows the user to select more than one file.
PropTypeDefaultDescription
childrenReact.ReactNode-Optional custom icon or content. Defaults to <Upload /> from lucide-react.
classNamestring-Additional class names for the logo container.

FileSelectText

PropTypeDefaultDescription
childrenReact.ReactNode-Text content to display.
classNamestring-Additional class names for the text container.

On this page