Skip to content
ilinxa/pro-ui

Carousel Composer

alphav0.2.0

Multi-item media post composer — drag in photos and videos, reorder them on a rail, and edit each through a shared editor panel.

Category: MediaUpdated: 2026-08-11Created: 2026-06-05Author: ilinxa

Context

The authoring counterpart to the media-carousel viewer. Composes the shipped media-editor WITHOUT modifying it: a single editor instance is loaded serially with the selected item (never N at once, so it can't trip media-editor's multi-instance guard). The procomp owns the collection concerns — file intake (drop + multiple browse, MIME-inferred so there are no photo/video capture tabs), the ordered MediaCarouselItem model, the @dnd-kit reorderable thumbnail rail, the main preview, and the edit-panel lifecycle (Edit → mount media-editor in edit-only mode → flatten export back into the item). Edits flatten on apply so the rail/preview are always publish-ready; export() is pull-only. v0.1 intake is upload-only (library clamped) and video items are preview/reorder/remove + limited edit. Output is shaped to feed media-carousel. Primary downstream consumer: content-composer's post media slot (wired in content-composer v0.2 via the mediaCarouselSlot substrate).

Installation

Initialize shadcn (once per project)Seeds lib/utils.ts and components.json. Skip if you've already used any shadcn component.
pnpm dlx shadcn@latest init
Register the @ilinxa namespace (once per project)Add to your components.json. Merge with existing config.
"registries": {
  "@ilinxa": "https://ui.ilinxa.com/r/{name}.json"
}
Install the component
pnpm dlx shadcn@latest add @ilinxa/carousel-composer

Add -fixtures for dummy data:

pnpm dlx shadcn@latest add @ilinxa/carousel-composer-fixtures

Preview

ridge-light.jpg

Seeded from remote URLs (CMS re-edit). Select a thumb, drag to reorder, press Edit on a photo. Video Edit is deferred to v0.2.

Demo source

demo.tsxtsx

Usage

When to use

Reach for CarouselComposer when an author needs to assemble one or more media items into an ordered set — an Instagram-style feed post, an album, a product gallery, or chat attachments. It mixes photos and videos in a single rail, reorders by drag-and-drop, and edits any item through a shared media-editor panel. For a single hero image/video, use media-editor directly; to display a finished set, use media-carousel.

Basic example

import { CarouselComposer } from "@/components/carousel-composer"

export function PostMedia() {
  const [items, setItems] = React.useState([])
  return (
    <CarouselComposer
      value={items}
      onChange={setItems}
      maxItems={10}
      editorProps={{ enabledTools: ["crop", "filters", "adjust"] }}
    />
  )
}

Pull-only export

Edits flatten into each item on Done, so the rail and preview always show publish-ready media. Call ref.current.export() at publish time to get the committed ordered array (an open, unapplied edit is excluded — gate your publish button while editing).

Notes

  • Intake is file-based — the file's MIME type decides image vs video, so there are no photo/video capture tabs.
  • All items share one aspect (Instagram behaviour). aspect defaults to "auto" (derived from item 1); override with a fixed AspectRatio.
  • Editing mounts a single media-editor instance, loaded serially per item — never N at once.
  • v0.1: video items support preview / reorder / remove; full video editing tracks media-editor maturity. "library" source is clamped to upload-only.
  • Object URLs created during intake/edit are revoked on remove, replace, reset, and unmount.

Features

  • Multi-file intake — drag-and-drop dropzone + multiple Browse, mixed image + video (MIME-inferred, no capture-mode tabs)
  • Ordered MediaCarouselItem model — add / remove / reorder / select
  • Reorderable thumbnail rail (@dnd-kit horizontal sortable) with pointer + keyboard reorder and a dedicated drag handle
  • Main preview of the selected item (image cover-fit; video on a black mat)
  • Single shared media-editor edit panel — Edit pushes the selected item in, flattens the export back on Done, reloads the same panel for the next item
  • Shared aspect across the carousel (Instagram behaviour) — `aspect="auto"` derives from item 1, overridable
  • Controlled / uncontrolled value (value / defaultValue / onChange) + imperative handle (getItems / export / addFiles / removeItem / select / openEditor / reset)
  • Pull-only export() — items already flattened on edit-apply
  • Per-file type + size validation; maxItems cap (default 10, Instagram parity)
  • Object-URL lifecycle managed (revoked on remove / replace / reset / unmount)

Tags

carousel-composercarouselgallerymediauploadereditordrag-and-dropinstagram

Dependencies

shadcn primitives: button, scroll-area
npm peer deps: @dnd-kit/core@^6.3.1, @dnd-kit/sortable@^10.0.0, @dnd-kit/utilities@^3.2.2, lucide-react@^1.11.0
internal: media-editor