# Your first Wrapped

This page goes from an empty file to a video that plays: who it is about, a seed, three scenes and one component, with the settings working. It takes one file of about seventy lines. Everything comes from `@react-wrapped/kit`, the package with all the others in it.

> Live preview: try it at https://bettertyped.github.io/react-wrapped/docs/first-wrapped/
>
> What the file on this page renders. Three scenes, and the settings beside them.

## 1. Install

```bash
pnpm add @react-wrapped/kit gsap
```

Load the stylesheet once, where your app starts:

```tsx
import "@react-wrapped/kit/styles.css";
```

The frame is set in Inter. [Installation](https://bettertyped.github.io/react-wrapped/docs/installation/) says how to load it.

## 2. Say who it is about

The subject is the person the year belongs to. The handle and the caption sit in the top corner of every frame.

```tsx
// Who the video is about. The handle and the caption sit in the corner of every frame.
const subject: WrappedSubject = {
  handle: "ada",
  name: "Ada",
  avatarUrl: null,
  caption: "Orbit Wrapped",
  period: "Oct 2025 to Oct 2026",
};
```

## 3. Bring your numbers

Scenes say what you hand them, so the numbers are yours to work out. Here they are made up: a count per day. Two of the scenes draw the year as a grid of cells, and `calendarFromCounts` turns the counts into that grid.

```tsx
// Your numbers. Here: a made-up count per day from 11 October 2025, quiet at weekends.
const counts = Array.from({ length: 365 }, (_unused, day) => (day % 7 < 2 ? 0 : (day * 5) % 9));
const total = counts.reduce((sum, count) => sum + count, 0);
const activeDays = counts.filter((count) => count > 0).length;
// The year as a grid of cells: a level from 0 to 4 per day, the weekday it starts on and the month names.
const calendar = calendarFromCounts(counts, new Date("2025-10-11"));
```

## 4. Place three scenes

A scene is a function. The first argument is what it says, the second is its key and its label on the timeline. The array is the video, in play order. These three are the [opening](https://bettertyped.github.io/react-wrapped/docs/scenes/opening/), a [big number](https://bettertyped.github.io/react-wrapped/docs/scenes/big-number/) and a [list](https://bettertyped.github.io/react-wrapped/docs/scenes/list/).

```tsx
// A scene is a function: what it says, then the key and the label it goes by on the timeline.
const scenes = [
  openingScene(
    { title: "My year on Orbit.", period: "@ada, Oct 2025 to Oct 2026", calendar, variant: 0 },
    { key: "opening", label: "Opening" },
  ),
  bigNumberScene(
    {
      eyebrow: "In the last 12 months you made",
      value: total,
      title: ["contributions."],
      lede: `On **${activeDays}** active days out of ${counts.length}.`,
      calendar,
      counter: "climb",
      variant: 0,
    },
    { key: "total", label: "Total contributions" },
  ),
  listScene(
    {
      eyebrow: "Where it happened",
      title: ["Your top", "repositories."],
      items: [
        { name: "ada/orbit-cli", value: 742, unit: "commits", meta: "TypeScript", color: "#3178c6" },
        { name: "ada/telemetry", value: 355, unit: "commits", meta: "Go", color: "#00add8" },
        { name: "ada/dotfiles", value: 203, unit: "commits", meta: "Shell", color: "#89e051" },
      ],
    },
    { key: "repositories", label: "Top repositories" },
  ),
];
```

Stars in a text are marks: single stars put words in the accent colour, double stars make them bold.

## 5. Render WrappedView

`WrappedView` is the whole page: the player, its timeline and the settings. It needs the subject, the scenes, a seed and a config. The seed picks the transitions between scenes, so use something that is the person's own, such as their handle and the year. The config is everything a viewer can change, and it lives in your state.

```tsx
export function FirstWrapped() {
  // Everything a viewer can change (palette, format, speed, hidden scenes, edited texts) is this one object.
  const [config, setConfig] = useState<WrappedConfig>(DEFAULT_WRAPPED_CONFIG);
  return (
    <WrappedView
      subject={subject}
      // The seed picks the transitions and the small variations. The same seed always plays the same video.
      seed="ada:2026"
      scenes={scenes}
      config={config}
      onConfigChange={setConfig}
    />
  );
}
```

That is a playing video. Hover the timeline to switch a scene off, move it, or open it and rewrite its headline. Change the palette and the format in the settings. All of it lands in `config`.

## The whole file

`first-wrapped.tsx`

```tsx
import { useState } from "react";

import {
  bigNumberScene,
  calendarFromCounts,
  DEFAULT_WRAPPED_CONFIG,
  listScene,
  openingScene,
  type WrappedConfig,
  type WrappedSubject,
  WrappedView,
} from "@react-wrapped/kit";

// Who the video is about. The handle and the caption sit in the corner of every frame.
const subject: WrappedSubject = {
  handle: "ada",
  name: "Ada",
  avatarUrl: null,
  caption: "Orbit Wrapped",
  period: "Oct 2025 to Oct 2026",
};

// Your numbers. Here: a made-up count per day from 11 October 2025, quiet at weekends.
const counts = Array.from({ length: 365 }, (_unused, day) => (day % 7 < 2 ? 0 : (day * 5) % 9));
const total = counts.reduce((sum, count) => sum + count, 0);
const activeDays = counts.filter((count) => count > 0).length;
// The year as a grid of cells: a level from 0 to 4 per day, the weekday it starts on and the month names.
const calendar = calendarFromCounts(counts, new Date("2025-10-11"));

// A scene is a function: what it says, then the key and the label it goes by on the timeline.
const scenes = [
  openingScene(
    { title: "My year on Orbit.", period: "@ada, Oct 2025 to Oct 2026", calendar, variant: 0 },
    { key: "opening", label: "Opening" },
  ),
  bigNumberScene(
    {
      eyebrow: "In the last 12 months you made",
      value: total,
      title: ["contributions."],
      lede: `On **${activeDays}** active days out of ${counts.length}.`,
      calendar,
      counter: "climb",
      variant: 0,
    },
    { key: "total", label: "Total contributions" },
  ),
  listScene(
    {
      eyebrow: "Where it happened",
      title: ["Your top", "repositories."],
      items: [
        { name: "ada/orbit-cli", value: 742, unit: "commits", meta: "TypeScript", color: "#3178c6" },
        { name: "ada/telemetry", value: 355, unit: "commits", meta: "Go", color: "#00add8" },
        { name: "ada/dotfiles", value: 203, unit: "commits", meta: "Shell", color: "#89e051" },
      ],
    },
    { key: "repositories", label: "Top repositories" },
  ),
];

export function FirstWrapped() {
  // Everything a viewer can change (palette, format, speed, hidden scenes, edited texts) is this one object.
  const [config, setConfig] = useState<WrappedConfig>(DEFAULT_WRAPPED_CONFIG);
  return (
    <WrappedView
      subject={subject}
      // The seed picks the transitions and the small variations. The same seed always plays the same video.
      seed="ada:2026"
      scenes={scenes}
      config={config}
      onConfigChange={setConfig}
    />
  );
}
```

## WrappedView props

| Prop | Type | What it does |
| --- | --- | --- |
| `subject` | `WrappedSubject` | Who the video is about. |
| `seed` | `string` | Picks the transitions and the small variations. Give each person their own. |
| `scenes` | `readonly SceneInstance[]` | The scenes in story order. |
| `config` | `WrappedConfig` | Everything a viewer can change. |
| `onConfigChange` | `(next: WrappedConfig) => void` | Called with the whole next config on every change. |
| `editable?` | `boolean` | Whether this is the creator. Off, there are no settings, no editing and no scan: only the video. On unless you say otherwise. |
| `share?` | `{ fileName: string; graphics?: readonly WrappedGraphic[]; onError?: (message: string) => void }` | Adds the link and download buttons, and the [gallery](https://bettertyped.github.io/react-wrapped/docs/graphics/gallery/) when there are graphics. |
| `opening?` | `{ calendar; drops; found; onOpen; describeDay; actions?; sessionKey?; gate? }` | Adds the [scan](https://bettertyped.github.io/react-wrapped/docs/scanner/) and the scenes hidden in the year. |
| `viewer?` | `ReactNode` | Shown under the video to someone who is not the creator, usually a way to make their own. |
| `autoplay?` | `boolean` | Starts playing as soon as the video is built. On unless you say otherwise. |
| `playerRef?` | `Ref<WrappedPlayerHandle \| null>` | The [player's handle](https://bettertyped.github.io/react-wrapped/docs/player/wrapped-player/): play, pause, seek. |
| `className?` | `string` | A class for the page's root. |
| `classNames?` | `SlotClassNames<WrappedSlot>` | A class per part, by slot name, for the page and everything on it: `"view-player-panel"`, `"player-play"`, `"settings-chip"`. See [styling](https://bettertyped.github.io/react-wrapped/docs/guides/styling/). |
| `loading?` | `undefined` | Not passed with a year. Pass it in place of the year and the view is the [loading page](https://bettertyped.github.io/react-wrapped/docs/player/loading/). |

## Where to go next

- **More scenes.** There are [eleven](https://bettertyped.github.io/react-wrapped/docs/scenes/), and the [chart scene](https://bettertyped.github.io/react-wrapped/docs/scenes/chart/) takes any of the [twenty-one charts](https://bettertyped.github.io/react-wrapped/docs/charts/).
- **A link that is the video.** Keep the [config in the URL](https://bettertyped.github.io/react-wrapped/docs/guides/config-in-the-url/) and a shared link plays exactly what its owner set.
- **Things to keep.** Pass `share` for the download button and the [graphics](https://bettertyped.github.io/react-wrapped/docs/graphics/).
- **Hidden scenes.** Pass `opening` and the [scanner](https://bettertyped.github.io/react-wrapped/docs/scanner/) finds them.
- **Your own page.** [Build the page from parts](https://bettertyped.github.io/react-wrapped/docs/guides/from-parts/) when `WrappedView` is not the layout you want.
- **All of it at once.** The [full example](https://bettertyped.github.io/react-wrapped/examples/full-wrapped/) is a whole year, directed from data.

## Related

- [Installation](https://bettertyped.github.io/react-wrapped/docs/installation/)
- [Scenes](https://bettertyped.github.io/react-wrapped/docs/scenes/)
- [Player](https://bettertyped.github.io/react-wrapped/docs/player/)
- [The config in the URL](https://bettertyped.github.io/react-wrapped/docs/guides/config-in-the-url/)
- [Full Wrapped example](https://bettertyped.github.io/react-wrapped/examples/full-wrapped/)

Source: https://bettertyped.github.io/react-wrapped/docs/first-wrapped/
