opensourceui
GitHubTwitter/X
  1. Home
  2. Components
  3. Gallery
  4. Film Strip
Back to Gallery

Components / Gallery

Film Strip

Free Gallery React component — FilmStripCard. MIT licensed, copy-paste ready for Next.js and Tailwind CSS.

Horizontal 35mm strip with sprocket holes and multiple frames. Roll label up top — great for photo galleries and vintage layouts.

Preview

Roll 24 · Kodak Portra

Frame 101
Frame 202
Frame 303
Frame 404

Setup

How to use

Free for personal and commercial use. No UI attribution required. Include the MIT copyright notice when copying component source into your project. Read the MIT license.

  1. 1

    Run in your terminal:

    $npm install clsx tailwind-merge lucide-react
  2. 2

    Copy lib/cn.ts below. Skip if you already have cn().

  3. 3

    Copy the code below and create components/gallery/film-strip-card.tsx in your project.

  4. 4

    Import and render:

    Example

    import { FilmStripCard } from "@/components/gallery/film-strip-card";

    <FilmStripCard />

lib/cn.ts

1
2
3
4
5
6
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
 
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

components/gallery/film-strip-card.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import Image from "next/image";
import { forwardRef, type ComponentPropsWithoutRef } from "react";
 
import { cn } from "@/lib/cn";
 
export type FilmStripCardProps = Readonly<
{
frames?: string[];
label?: string;
} & ComponentPropsWithoutRef<"div">
>;
 
const defaultFrames = [
"/wallpaper-2.png",
"/wallpaper-3.png",
"/wallpaper-11.png",
"/wallpaper-15.png",
];
 
const LEFT_SPROCKET_HOLES = [
"left-sprocket-1",
"left-sprocket-2",
"left-sprocket-3",
"left-sprocket-4",
"left-sprocket-5",
"left-sprocket-6",
"left-sprocket-7",
"left-sprocket-8",
] as const;
 
const RIGHT_SPROCKET_HOLES = [
"right-sprocket-1",
"right-sprocket-2",
"right-sprocket-3",
"right-sprocket-4",
"right-sprocket-5",
"right-sprocket-6",
"right-sprocket-7",
"right-sprocket-8",
] as const;
 
// Production-ready Film Strip component — styled with Tailwind CSS.
export const FilmStripCard = forwardRef<HTMLDivElement, FilmStripCardProps>(
(
{
className,
frames = defaultFrames,
label = "Roll 24 · Kodak Portra",
...props
},
ref,
) => (
<div
ref={ref}
data-slot="film-strip-card"
className={cn("w-sm font-mono", className)}
{...props}
>
<p className="mb-2 text-center text-[9px] tracking-widest text-neutral-400 uppercase">
{label}
</p>
<div className="relative rounded-sm bg-neutral-900 px-2 py-3 shadow-lg">
<div className="absolute top-0 bottom-0 left-0 flex w-3 flex-col justify-evenly">
{LEFT_SPROCKET_HOLES.map((holeId) => (
<div
key={holeId}
className="mx-auto h-2 w-1.5 rounded-sm bg-neutral-700"
/>
))}
</div>
<div className="absolute top-0 right-0 bottom-0 flex w-3 flex-col justify-evenly">
{RIGHT_SPROCKET_HOLES.map((holeId) => (
<div
key={holeId}
className="mx-auto h-2 w-1.5 rounded-sm bg-neutral-700"
/>
))}
</div>
<div className="mx-4 flex gap-1 overflow-hidden">
{frames.map((src, frameNumber) => (
<div
key={src}
className="relative h-20 w-16 shrink-0 overflow-hidden border-2 border-neutral-800 sm:h-24 sm:w-20"
>
<Image
src={src}
alt={`Frame ${frameNumber + 1}`}
fill
sizes="80px"
className="object-cover"
/>
<span className="absolute bottom-0.5 left-0.5 bg-black/60 px-1 text-[7px] text-white">
{String(frameNumber + 1).padStart(2, "0")}
</span>
</div>
))}
</div>
</div>
</div>
),
);
 
FilmStripCard.displayName = "FilmStripCard";
PreviousNext

On this

page

Jump to a section on this page.

  • lib/cn.ts
  • components/gallery/film-strip-card.tsx

Sponsor spot · demo preview

Logo

Your brand name

Your tagline or category

A short pitch about your product or service goes here. This is a preview of how sponsor cards will look in this sidebar.

Your call to action

This slot is available. Sponsor Opensource UI and reach developers browsing components every day.