SSGOI

June 28, 2026 · View on GitHub

Native app-like page transitions for the web.

Try it live →

SSGOI Demo


AI-Assisted Setup

Using Claude, Cursor, ChatGPT, or other AI assistants? Let them set it up for you.

Add this to your AI's context:

https://ssgoi.dev/llms.txt

Contains complete setup guides, all transition types, troubleshooting, and API docs.


Why SSGOI?

Web pages don't transition—they just swap. SSGOI changes that.

View Transition APIOther LibrariesSSGOI
All browsers❌ Chrome only
SSR support⚠️ Limited⚠️ Varies
Spring physics⚠️ Some
Router agnostic
Back/forward state

60fps guaranteed — Spring physics pre-computed to Web Animation API keyframes. GPU-accelerated, main thread free.


Quick Start

npm install @ssgoi/react

1. Wrap your React app

// app/ssgoi-provider.tsx
"use client";

import { type ReactNode } from "react";
import { Ssgoi } from "@ssgoi/react";
import { drill, fade } from "@ssgoi/react/view-transitions";
import { SsgoiTransitionBoundary } from "./ssgoi-transition-boundary";

const config = {
  transitions: [
    // iOS-style drill-in when entering a post, ease back out
    drill({ enter: "/post/*", exit: "*" }),
    // Calm cross-fade between top-level pages
    fade({ paths: ["/", "/about"] }),
  ],
};

export function SsgoiProvider({ children }: { children: ReactNode }) {
  return (
    <Ssgoi config={config}>
      {/* Routed content marker. Layout positioning belongs to the outer wrapper. */}
      <SsgoiTransitionBoundary className="min-h-full bg-black">
        {children}
      </SsgoiTransitionBoundary>
    </Ssgoi>
  );
}

// app/layout.tsx
import { type ReactNode } from "react";
import { SsgoiProvider } from "./ssgoi-provider";

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>
        {/* Layout shell: positioned ancestor + stacking context for the OUT clone. */}
        <main className="relative z-0 min-h-dvh overflow-x-clip bg-black">
          <SsgoiProvider>{children}</SsgoiProvider>
        </main>
      </body>
    </html>
  );
}

2. Use a React boundary utility

// ssgoi-transition-boundary.tsx
"use client";

import { type ElementType, type ReactNode } from "react";
import { usePathname } from "next/navigation";

export function SsgoiTransitionBoundary({
  children,
  as,
  className,
}: {
  children: ReactNode;
  as?: ElementType;
  className?: string;
}) {
  const pathname = usePathname();
  const Component = as ?? "div";

  return (
    <Component
      key={pathname}
      data-ssgoi-transition={pathname}
      className={className}
    >
      {children}
    </Component>
  );
}

Then use it inside <Ssgoi>:

<SsgoiTransitionBoundary className="min-h-full bg-black">
  {children}
</SsgoiTransitionBoundary>

React Router and TanStack Router use the same component body with their own pathname hook.

The utility reads the current pathname internally and sets key plus data-ssgoi-transition. This is a React convenience pattern: SSGOI only needs a logical page id that matches config, and the utility uses the current pathname as that id. Match dynamic descendants with wildcard config like /post/*. Use /docs/** when the parent path itself should match too. That's it. Your pages now transition like a native app.

This layout-level utility pattern is for React adapters. In SvelteKit, Nuxt/Vue, Solid, Angular, and Qwik, mark each routed page boundary directly with data-ssgoi-transition. Use a stable logical id such as /gallery; it does not have to be the actual route pathname.

Why mark each page directly instead of using one shared boundary? SvelteKit and Nuxt render slot/snippet content live, so a single route boundary in a parent layout would let the outgoing page wrapper render the incoming page's children mid-navigation. Marking each page keeps the outgoing and incoming boundaries cleanly separated. React's utility sidesteps this by keying the subtree on the pathname.

Layout shell requirements

The outer element that wraps the SSGOI provider / <Ssgoi> is the layout shell. Put relative z-0 on that wrapper, and add overflow-x-clip when you use horizontal transitions such as slide or drill.

Those classes are not route marker classes. Keep them off SsgoiTransitionBoundary; the boundary should only identify routed content with key and data-ssgoi-transition.

ClassWhy
relativeThe OUT page clone uses position: absolute, so it needs a positioned ancestor
z-0Creates a stacking context so the OUT page does not fall behind backgrounds
overflow-x-clipPrevents horizontal overflow flashing during slide/drill transitions

Framework templates

Use the templates as reference implementations for each router/framework:

React templates use the pathname boundary utility. SolidStart, SvelteKit, Nuxt, Qwik, Solid, and Angular mark each routed page directly with data-ssgoi-transition.


Transitions

Each transition is a factory you drop into config.transitions. They return path-transition groups, so nested arrays are flattened automatically.

import {
  fade,
  drill,
  slide,
  scroll,
  axis,
  sheet,
  hero,
  zoom,
} from "@ssgoi/react/view-transitions";

// Symmetric — every pair animates the same ({ paths })
fade({ paths: ["/", "/about"] });
hero({ paths: ["/products", "/products/*"] }); // shared element
zoom({ paths: ["/gallery", "/photo/*"], type: "expand" }); // card → detail

// Directional — enter / exit get different physics ({ enter, exit })
drill({ enter: "/post/*", exit: "*" }); // iOS list → detail
sheet({ enter: "/compose", exit: "*" }); // bottom sheet

// Ordered — path order decides forward / back ({ paths })
slide({ paths: ["/tabs/a", "/tabs/b"] }); // horizontal tabs
scroll({ paths: ["/step-1", "/step-2"] }); // vertical onboarding
axis({ paths: ["/feed", "/profile"] }); // Material shared axis

All built-in transitions: fade · drill · slide · scroll · axis · sheet · hero · zoom · strip · blind · film · rotate · jaemin.

See them all live at ssgoi.dev or in llms.txt.


Packages

PackageFramework
@ssgoi/reactReact, Next.js
@ssgoi/svelteSvelte, SvelteKit
@ssgoi/vueVue, Nuxt
@ssgoi/solidSolid, SolidStart
@ssgoi/angularAngular
@ssgoi/qwikQwik, Qwik City
@ssgoi/coreFramework-agnostic engine

Documentation

ssgoi.dev — Full docs, interactive examples, and API reference. ssgoi.dev/llms.txt — Plain-text setup guide for AI assistants.


License

MIT © MeurSyphus