This component no longer exists. It was in remotionui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-11 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
bento-pan
remotionui/bento-panRemovedBlock
Diagonal bento grid pan
Live preview
live · rendered by the registry
Rendered by remotionui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://remotionui.com/r/bento-pan.jsonSource
1import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";2import { EASING_EXIT } from "@/remotion/lib/timing";34export type BentoTile = {5 w: number;6 h: number;7 color: string;8};910export type BentoPanProps = {11 backgroundColor?: string;12 tiles?: BentoTile[];13};1415const DEFAULT_TILES: BentoTile[] = [16 { w: 32, h: 28, color: "rgba(232,184,109,0.2)" },17 { w: 28, h: 36, color: "rgba(45,212,191,0.18)" },18 { w: 36, h: 30, color: "rgba(244,114,182,0.16)" },19 { w: 30, h: 34, color: "rgba(245,158,11,0.18)" },20];2122export const BentoPan: React.FC<BentoPanProps> = ({23 backgroundColor = "#080810",24 tiles = DEFAULT_TILES,25}) => {26 const frame = useCurrentFrame();27 const { fps, width, height } = useVideoConfig();2829 // Enter + hold-with-life: the camera keeps drifting across the grid for30 // the whole hold instead of stopping dead once the initial pan lands.31 const panProgress = interpolate(frame, [0, 130], [0, 1], {32 extrapolateLeft: "clamp",33 extrapolateRight: "clamp",34 });35 const panX = panProgress * -width * 0.26;36 const panY = panProgress * -height * 0.14;3738 // A slow breathing rotation keeps the hold visually alive between the pan39 // settling and the exit beat kicking in.40 const breathe = Math.sin((frame / fps) * Math.PI * 0.6) * 0.6;4142 // Exit: the grid settles a touch closer and fades through the vignette43 // rather than freezing on the final pan position. Opacity fades linearly44 // so the beat reads clearly through the whole window (an ease-in curve45 // here would keep it near-invisible until the last few frames), and it46 // fully resolves a few frames before the composition ends.47 const exitWindowStart = 130;48 const exitWindowEnd = 172;49 const exit = interpolate(frame, [exitWindowStart, exitWindowEnd], [0, 1], {50 extrapolateLeft: "clamp",51 extrapolateRight: "clamp",52 });53 const exitScale = interpolate(exit, [0, 1], [1, 1.08], {54 easing: EASING_EXIT,55 });56 const exitOpacity = 1 - exit;5758 return (59 <AbsoluteFill style={{ background: backgroundColor, overflow: "hidden" }}>60 <div61 style={{62 position: "absolute",63 inset: "-20%",64 display: "grid",65 gridTemplateColumns: "repeat(4, 1fr)",66 gap: 24,67 opacity: exitOpacity,68 transform: `translate(${panX}px, ${panY}px) scale(${exitScale}) rotate(${-6 + breathe}deg)`,69 }}70 >71 {Array.from({ length: 16 }, (_, i) => {72// … truncated
