# Polaroid
URL: https://remocn.dev/docs/ui-blocks/polaroid

An instant-photo frame with a handwritten caption for video, images, or live compositions

- Install: `npx shadcn@latest add @remocn/polaroid`
- Vibe: paper
- Natural length: 150f @ 30fps

## Installation

```bash
npx shadcn@latest add @remocn/polaroid
```

## Usage

The media is `children`, so anything can play inside the window. Give your
media `width: 100%` and `height: 100%` — the frame sizes the window, the
`objectFit` is yours.

```tsx
// src/Root.tsx
import { AbsoluteFill, Composition, OffthreadVideo, staticFile } from "remotion";
import { Polaroid } from "@/components/remocn/polaroid";

const PolaroidScene = () => (
  <AbsoluteFill
    style={{
      background: "#f1eee7",
      alignItems: "center",
      justifyContent: "center",
    }}
  >
    <Polaroid caption="first light">
      <OffthreadVideo
        src={staticFile("clip.mp4")}
        muted
        style={{ width: "100%", height: "100%", objectFit: "cover" }}
      />
    </Polaroid>
  </AbsoluteFill>
);

export const RemotionRoot = () => (
  <Composition
    id="Polaroid"
    component={PolaroidScene}
    durationInFrames={150}
    fps={30}
    width={1280}
    height={720}
  />
);
```

### A living photograph

The window takes a whole composition, not just a file. The scene inside keeps
running on the real 30fps clock while the frame around it moves in stop-motion
poses — a photograph that never stopped playing, with no pre-rendered video.

```tsx
<Polaroid caption="the demo">
  <YourProductScene />
</Polaroid>
```

### Stopping the photo

Wrap the media in Remotion's `<Freeze>` to make the photograph stop playing —
the usual reason is that the card is being flipped aside and should read as a
still print again.

```tsx
import { Freeze } from "remotion";

const FREEZE_AT = 90;

<Polaroid caption="the demo">
  <Freeze frame={FREEZE_AT} active={frame >= FREEZE_AT}>
    <YourProductScene />
  </Freeze>
</Polaroid>;
```

The card has no jitter or positioning of its own. Wrap it in `paper-wobble` to
put it on the desk, and position it yourself.

Every measurement derives from `width`, so the card keeps the proportions of
the original 652px print at any size. `captionSize` defaults to the same scale;
pass it explicitly only when you want the caption off that ratio.

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `children` (required) | `React.ReactNode` | — | The media inside the window; give it width and height of 100% |
| `caption` | `string` | — | Handwritten caption; omit it and the band stays empty |
| `captionAt` | `number` | `0` | Local frame the caption starts writing |
| `width` | `number` | `652` | Card width; every other measurement scales from it |
| `frameColor` | `string` | `"#fdfcf8"` | Card stock colour |
| `captionColor` | `string` | `"#26242c"` | Ink colour of the caption |
| `captionSize` | `number` | `scales from width` | Caption font size; defaults to 36 at the default width |
| `step` | `number` | `3` | Frames per stop-motion pose, forwarded to the caption |

## Use when
- Media belongs in a hand-placed instant-photo frame with a handwritten caption.
- You want a live composition to keep playing inside a stop-motion scene, with no pre-rendered file.
- The photograph should stop being live at some point — wrap the child in Remotion's `<Freeze>`.

## Don't use when
- You just need a plain rounded video frame with no paper story — use `backdrop` with padding.
- The card needs to wobble or sit at an angle: this component has no jitter and no positioning. Wrap it in `paper-wobble` and place it yourself.