Command Palette

Search for a command to run...

Media Viewer

A full-screen media lightbox that pages through a mixed list of images, videos, and audio in one swipeable carousel. Images fit the viewport, videos play inline with native controls, and audio slides show a waveform player under an optional cover that scales while it plays. Renders through a portal with its own fade, scroll lock, Escape-to-close, and slide counter — no dialog dependency. Controlled via open / onOpenChange with an initialIndex.

A full-screen media lightbox that pages through a mixed list of images, videos, and audio in a single swipeable carousel. Images fit to the viewport, videos play inline with native controls, and audio slides show a compact waveform player under an optional cover that scales while it plays.

It renders through a portal with its own fade in/out, background scroll lock, Escape-to-close, and a slide counter — no dialog dependency. Drive it as a controlled overlay with open / onOpenChange, and jump straight to any item with initialIndex.

Installation

One-time setup: add the @ikui registry to your components.json.

components.json
{
  "registries": {
    "@ikui": "https://ik-ui.pages.dev/r/{name}.json"
  }
}

Then install the component:

pnpm dlx shadcn@latest add @ikui/media-viewer

Usage

import { useState } from "react";
import { MediaViewer, type MediaViewerItem } from "@/components/media-viewer";

const items: MediaViewerItem[] = [
  { id: 1, type: "IMAGE", url: "/photo-1.jpg" },
  { id: 2, type: "VIDEO", url: "/clip.mp4", poster: "/clip-poster.jpg" },
  { id: 3, type: "AUDIO", url: "/track.mp3", poster: "/cover.jpg" },
];

function Gallery() {
  const [open, setOpen] = useState(false);
  const [index, setIndex] = useState(0);

  return (
    <>
      <button onClick={() => { setIndex(0); setOpen(true); }}>Open</button>
      <MediaViewer
        open={open}
        onOpenChange={setOpen}
        items={items}
        initialIndex={index}
      />
    </>
  );
}

Props

PropTypeDefault
open
boolean
-
onOpenChange
(open: boolean) => void
-
items
MediaViewerItem[]
-
initialIndex
number
0
loop
boolean
true

MediaViewerItem

PropTypeDefault
id
string | number
-
type
"IMAGE" | "VIDEO" | "AUDIO"
-
url
string
-
poster
string
-