Inspiration
I wanted a media library I could actually drop into any React app without dragging in someone else’s design system or a 300kb bundle. Most existing solutions are either tightly coupled to a UI kit, or feel heavy and slow. My goal was a lightweight, fast, headless React media library that still feels rich: drag-and-drop uploads, inline image editing, and a UI that can match whatever design system the app already uses.
What it does
React Media Library is a headless media library for React that you can mount anywhere:
- Headless core with a simple provider + hooks API
- Pluggable UI presets (e.g. Tailwind-first preset)
- Drag-and-drop uploads and grid browsing
- Image editor entry point (crop/zoom/etc.)
- Swappable icon set (Lucide, your own SVGs, or no icons at all)
- Designed to hit a perfect Lighthouse performance score in a minimal playground
You can use the headless core and build your own UI, or start with the bundled preset and customize from there.
How we built it
I started the project directly in Kiro as a conversation: I described the “headless media library” idea, the need for lightweight performance, and that I eventually wanted a perfect Lighthouse score for the demo.
From there:
- I turned that initial chat into steering docs inside Kiro: what “headless” means for this project, what tech stack is allowed (React + TypeScript, no huge dependencies), and the performance constraints.
- Once the direction was clear, I wrote specs for each feature: the provider API, grid behavior, upload flow, and theming/preset layer. Kiro used those specs to generate the initial components and wiring while keeping me in control of the architecture.
- I wired the library into a tiny Vite playground app and a Storybook setup so I could iterate on both DX and Lighthouse performance. Kiro helped refine the configs (Tailwind, PostCSS, tsup build, exports) while I kept an eye on bundle size and render behavior.
Kiro handled a lot of the repetitive code generation and refactors, while I focused on keeping the library API clean, headless, and fast.
Challenges we ran into
Headless but not painful
Designing an API that’s truly headless (no hardcoded styling or icon library) but still easy to adopt took a few iterations. Passing icon components instead of JSX, and offering presets as separate exports, solved most of this.Optional Lucide without bloating the bundle
I didn’t wantlucide-reactto be forced into every consumer’s bundle. The solution was to keep the core completely icon-agnostic and ship a separatepresetsentry that wires Lucide only if you import it.Lighthouse vs. Storybook overhead
Running Lighthouse straight on Storybook made scores look worse than they should, because Storybook adds its own runtime. The fix was a dedicated Vite playground app and a CLI script that runs Lighthouse against that minimal shell.Tailwind and content paths in a multi-folder setup
Making Tailwind pick up classes across the playground and the librarysrcrequired carefulcontentconfiguration and making sure Tailwind/PostCSS configs were actually being read (ESM vs CJS gotcha).
Accomplishments that we’re proud of
- A clean headless API that can be themed with Tailwind, shadcn, MUI, or custom CSS.
- A default Tailwind preset that looks good out of the box but stays small and unopinionated.
- Keeping Lucide completely optional, so the core library doesn’t force an icon library.
- A Vite playground + Lighthouse CLI flow that can reach a 100/100 performance score while still demoing a realistic media grid.
- Using Kiro effectively: starting from free-form “vibe coding” chat, then locking down behavior in steering docs and specs so the generated code stays aligned with the performance and API constraints.
What we learned
- Headless design forces you to think very clearly about data vs presentation. Once the boundary is right (provider + hooks + props), presets become much easier to implement.
- Performance needs to be part of the spec: deciding early what tech is allowed, what dependencies are off-limits, and how Lighthouse will be run kept the project light.
- Kiro works best when it’s fed structure: starting loose is fine, but the real gains came after I wrote steering docs and concrete specs for each feature. That’s where spec-driven development paid off.
- Having a dedicated playground app, instead of only Storybook, makes perf tuning and Lighthouse checks feel much closer to real-world usage.
What's next for React Media Library
- More UI presets (shadcn, MUI, Radix) built on the same headless core.
- A richer image editor flow (crop, rotate, aspect presets, simple adjustments).
- Better a11y and keyboard navigation for the grid and selection states.
- Hooks and adapters for plugging into real backends (Supabase, S3, etc.) while keeping the default demo backend-light.
- Turning the playground into a public reactkits.dev–style demo hub that showcases the media library alongside other React marketing widgets.
Built With
- kiro
- react
- tailwind
- typescript
Log in or sign up for Devpost to join the conversation.