DKMediaViewer
DKMediaViewer handles both the grid and lightbox on my photos page, and now it is available to download as a reusable component. It starts with a masonry grid, and when a photo is clicked it animates into a modal that supports swipe and arrow-key navigation. The DK prefix is of course an homage to NeXTSTEP’s NS prefixes.
Try it
Click a photo and tap the ← → arrow keys. Tap esc, click on a photo, or click × to close.
Install
npx shadcn@latest add @diklein/dk-media-viewerDKMediaViewer is distributed through the shadcn registry. The CLI places the source code into your project, and there is no package to depend on. React 18+ and Tailwind CSS v4 are required.
Use
import { DKMediaViewer } from '@/components/dk-media-viewer/dk-media-viewer'
import '@/components/dk-media-viewer/dk-media-viewer.css'
import items from '@/lib/media-items.json'
export default function Gallery() {
return <DKMediaViewer items={items} />
}Everything after src is optional and degrades elegantly.
{
src: '/photos/golden-gate.jpg',
alt: 'Golden Gate Bridge in fog',
caption: 'Marin Headlands, October.',
width: 2048, height: 1365,
exif: {
make: 'Ricoh', model: 'GR IIIx',
exposureTime: '1/500', aperture: '2.8',
focalLength: '26.1', iso: 200,
},
videoSrc: '/photos/clip.mp4', // optional: makes it a video (src = poster)
blurHash: 'LEHV6nWB2yk8pyo0adR*.7kCMdnj', // optional: a blurred preview while it loads
}You can also point it at a folder
Point the CLI at a folder filled with photos, and media-items.json is generated automatically.
npx @diklein/dkmediaviewer scan ./public/photos --out src/lib/media-items.jsonUse two files per photo for high resolution
The grid and the lightbox share src, so a somewhat large image just works. If a photo is gigantic, use two sizes. The grid will show the small file and the lightbox will show the large one. The lightbox secretly fades in the larger image as soon as it’s ready.
// A CDN or image optimizer: rewrite the small render
<DKMediaViewer
items={items}
getHiResSrc={(item) => item.src.replace('w=800', 'w=2048')}
/>
// Plain files: photos/thumbs/ in the grid, photos/full/ in the lightbox
<DKMediaViewer
items={items}
getHiResSrc={(item) => item.src.replace('/thumbs/', '/full/')}
/>Carousel
A second component, DKCarousel, provides you with a crossfading slideshow that auto-advances. Click a slide to view the series of photos in the lightbox.
Mirror
Details
- —The lightbox swaps to a larger image if available without any flashes.
- —The modal follows strict accessibility requirements.
- —Light and dark modes are supported using the
.darkclass. - —
prefers-reduced-motionis honored everywhere to prevent autoplay and introduce instant transitions. - —Videos keep playing through the open animation. This was inspired by the minimize feature Steve Jobs demonstrated in the Mac OS X introduction in 2000.
Easter egg
Press a Shift key and click on a photo (or arrow key inside the lightbox), and the animation runs at one-tenth speed. When Mac OS X 10.3 launched, the similar feature to make dock minimizing move in slow motion expanded to support Exposé. It was extremely cool. Try it on the grid above.
Theming
.dk-scope {
--dk-accent: #0f62fe; /* video progress bar */
--dk-font-mono: 'Berkeley Mono', ui-monospace, monospace; /* the EXIF line */
}