Installation
npx shadcn@latest add https://ui.srb.codes/r/folder.json
Usage
"use client";
import { Folder } from "@/components/folder";
export function Hero() {
return <Folder size={56} />;
}Patterns
Driving hover from a parent
The folder tracks its own hover state by default, but you can lift it up when the open animation should fire from a different element — for example, a pill that contains the folder plus a label.
"use client";
import * as React from "react";
import { Folder } from "@/components/folder";
export function FolderPill() {
const [hovered, setHovered] = React.useState(false);
return (
<button
type="button"
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
className="inline-flex items-center gap-2 rounded-full border px-3 py-1"
>
<Folder size={24} isHovered={hovered} />
<span>New components every week</span>
</button>
);
}When you pass isHovered, the component stops listening to its own pointer events and follows the prop instead.
Custom files
Pass any three (or more) image URLs and they'll fan out in the order given. Smaller folders look best with smaller images, so keep aspect ratios uniform.
<Folder
size={64}
files={[
"/screenshots/inbox.png",
"/screenshots/calendar.png",
"/screenshots/notes.png"
]}
/>Icon on the front face
Drop any React node into icon to label the folder — typically a Lucide icon, but a string, an emoji, or an inline SVG all work. Lucide-style SVGs are auto-sized to size-5 and tinted to match the folder; pass a node with explicit size/color classes to override.
import { ImageIcon } from "lucide-react";
<Folder size={56} icon={<ImageIcon />} />;The icon is mounted inside the front face, so it tilts and scales with the open animation — it shrinks slightly as the folder fans open, then settles back when the cursor leaves. The wrapper is pointer-events-none and aria-hidden, so the icon is purely decorative and never steals hover from the folder itself.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
size | number | 62 | Pixel height of the rendered folder. The component scales the natural 72×62 artwork uniformly so it stays crisp at any size. |
isHovered | boolean | — | Force the open/closed state from outside. When omitted, the folder tracks its own pointer hover. |
files | string[] | three picsum placeholders | Image URLs that pop out of the folder. The first three are positioned to fan out on hover; additional entries stack behind the leading file. |
icon | React.ReactNode | — | Optional icon (or any node) rendered centered on the folder's front face. Lucide SVGs auto-size to size-5 and tint white. |
className | string | — | Forwarded to the outer wrapper. |
Sizing
The artwork has a natural footprint of 72×62 pixels (a 54-tall body plus an 8-tall tab on top). size is interpreted as a target height in pixels and the whole composition scales uniformly — so passing size={24} renders a folder that occupies roughly 28×24 px and size={120} gives you a 140×120 hero element. The CSS box that the folder reserves matches the scaled dimensions, so it lays out predictably inside flex and inline-flex parents.
Theming
The folder uses Tailwind's blue palette directly (from-blue-300/400/500 with a darker dark: ramp) rather than CSS variables, because it's intentionally decorative — the goal is to look the same across themes, not to follow the active accent color. To recolor, fork the component and swap the from-*/to-* classes; everything else (shadows, ring, file thumbnails) is theme-neutral.