SSGOI

zoom

The whole detail page unfolds from the selected card or image, using that element as its spatial anchor.

Drill-in — List → detail, going one level deeper.

When this motion fits

When to use
Use it when a selected card or image should visibly open into its dedicated detail page.
What moves
One matched source expands toward the destination while the rest of the page follows the chosen type.
Avoid when
Avoid it when several shared elements should move together, or when the detail page cannot provide exactly one destination marker.

Add it to a route rule

zoom() sets the motion; the rule around it decides which navigations get it. The config shape is the same in every framework.

import { zoom } from "@ssgoi/react/view-transitions";

const config = {
  transitions: [
    {
      from: "/list",
      to: "/detail/*",
      transition: zoom(),
    },
  ],
};

Pattern forms, priority and the scroll defaults that come with each rule shape are on Route rules.

Connect the source to the destination

The value identifies the selected entity. The destination key must exactly match the source card or image that opened it.

PageAttribute on the element
Source pagedata-zoom-exit-key="same-id"
Destination pagedata-zoom-enter-key="same-id"
  • A list may contain many exit markers as long as each key is unique.
  • The detail page must contain exactly one enter marker.
  • If the same exit key is duplicated, the first element in DOM order is used.
An empty, missing, or mismatched key is a no-op. Zero or more than one enter marker on the destination is also a no-op.
{/* Source: many unique cards are allowed */}
<img
  data-zoom-exit-key={photo.id}
  src={photo.thumbnail}
  alt={photo.alt}
/>

{/* Destination: exactly one enter marker */}
<img
  data-zoom-enter-key={photo.id}
  src={photo.full}
  alt={photo.alt}
/>

Configurations

type, variant and option are independent. Each clip is recorded from a demo published on ssgoi.dev. Combinations without a published demo keep the API details without a substitute clip.

  • static · default

    type: staticvariant: defaultdefault

    The originating surface stays visually steady while the selected content opens above it. Controls drawn over the shared image, and any list chrome the card sits under (a tab bar, a badge), crossfade with the tile instead of popping when it lands.

    A selected post opens into its detail page over a visually steady background.
    zoom({ type: "static" })

    Open live demoOpens at the source screen

  • static · fade

    type: staticvariant: fade

    Fades the whole detail page around the shared image, not only what overlaps it, while retaining the same anchored zoom.

    zoom({ type: "static", variant: "fade" })
  • expand · default

    type: expandvariant: default

    The selected card grows into the detail surface, making containment feel explicit.

    A selected card expands until it becomes the full detail surface.
    zoom({ type: "expand" })

    Open live demoOpens at the source screen

  • expand · fade

    type: expandvariant: fade

    Keeps the card-to-surface expansion but fades surrounding content for a softer reveal.

    zoom({ type: "expand", variant: "fade" })

    Open live demoOpens at the source screen

  • blur · default

    type: blurvariant: default

    The background loses focus as the selected content advances, creating a strong focus pull.

    zoom({ type: "blur" })
  • blur · fade

    type: blurvariant: fade

    Combines the focus-pull blur type with the optional fade modifier.

    A listing image zooms into detail as the background blurs and fades.
    zoom({ type: "blur", variant: "fade" })

    Open live demoOpens at the source screen

Agent guide: /llms/transitions/zoom.txt — every prop, the required markup, and a config fragment as plain text.

Read next