vt-helper
A tiny, zero-dependency helper for the browser View Transitions API, with automatic fallback for unsupported browsers.
Why
document.startViewTransition is great, but every call site ends up rewriting the same
feature-detection boilerplate. vt-helper wraps it once so your code looks the same
whether or not the browser supports it — on unsupported browsers your DOM update still runs,
you just lose the animation.
There's no shortage of View Transitions tooling, but most of it is either a low-level toolkit or
locked to one framework. vt-helper is neither: it's a tiny, framework-agnostic wrapper
focused on one job — making startViewTransition and multi-step sequencing safe to
call everywhere, with zero dependencies.
| Package | What it does | Where it doesn't fit |
|---|---|---|
| view-transitions-toolkit (Chrome Labs) |
Full toolkit: feature detection, animation optimization, playback control, temporary names | Low-level and broad in scope — more infrastructure than a drop-in helper |
next-view-transitions |
View transitions for Next.js App Router | Next.js only |
use-view-transitions |
React hook | React only |
vue-view-transitions |
Vue composable | Vue only |
Motion animateView() |
Wraps view transitions with extra animation features | Pulls in the Motion library |
React <ViewTransition> (canary) |
Native React support | Experimental, React-only |
Chrome's transitionHelper snippet |
Minimal feature-detection helper from Chrome's docs | Not a package — no sequencing, no types, copy-pasted per project |
vt-helper fills the gap between "copy a snippet from Chrome's docs" and "adopt a whole
toolkit or framework binding":
- Framework-agnostic — plain DOM API, works with React, Vue, Svelte, or vanilla JS
- Zero dependencies — nothing to audit, nothing to pull in transitively
- Same shape, always — the fallback returns a
TransitionHandlewith the samefinished/ready/updateCallbackDone/skipTransition()shape as the nativeViewTransition, so calling code never branches on support - Sequencing built in —
transitionSequence()chains steps without overlap, which the native API and most wrappers leave for you to implement - Small and typed — a couple hundred lines of TypeScript, fully typed, tree-shakeable
Install
npm install vt-helper
import { transition } from 'vt-helper';
button.addEventListener('click', () => {
transition(() => {
document.body.classList.toggle('dark');
});
});
Live examples
These demos run vt-helper loaded straight from a CDN, right here in the page.
1. Theme toggle
Wrap a state change in transition(). Supported browsers cross-fade the change automatically; the fallback just applies the class.
Card title
Click the button to toggle dark mode with a view transition.
import { transition } from 'vt-helper';
card.style.viewTransitionName = 'demo-toggle'; // scope the animation
button.addEventListener('click', () => {
transition(() => {
card.classList.toggle('dark');
});
});
2. Reorder a list
Each list item keeps a stable view-transition-name, so the browser animates items sliding to their new position.
- Alpha
- Bravo
- Charlie
button.addEventListener('click', () => {
transition(() => {
list.append(list.firstElementChild); // move first item to the end
});
});
3. Sequenced steps
transitionSequence() runs each step's transition only after the previous one has fully finished, so steps never overlap.
import { transitionSequence } from 'vt-helper';
await transitionSequence([
() => setStep(1),
() => setStep(2),
() => setStep(3),
]);
API reference
transition(callback)
→ TransitionHandle
Wraps document.startViewTransition(callback). callback may be sync or return a Promise.
On unsupported browsers, runs callback via requestAnimationFrame (falling back to a microtask
outside the DOM) and returns a handle whose promises resolve once the callback settles.
interface TransitionHandle {
readonly finished: Promise<void>;
readonly ready: Promise<void>;
readonly updateCallbackDone: Promise<void>;
skipTransition(): void;
}
On unsupported browsers, ready, finished, and updateCallbackDone all resolve (or reject) together once callback settles, and skipTransition() is a no-op.
transitionSequence(callbacks)
→ Promise<void>
Runs an array of TransitionCallbacks in order, calling transition() for each and awaiting its
finished promise before starting the next. If a step throws or rejects, the sequence stops and the
returned promise rejects with that error.
isViewTransitionsSupported()
→ boolean
Feature-detects document.startViewTransition. Useful if you want to branch your own logic instead of relying on the built-in fallback.
| Export | Kind | Description |
|---|---|---|
transition | function | Run a single transition step. |
transitionSequence | function | Run steps one after another, never overlapping. |
isViewTransitionsSupported | function | Feature detection for document.startViewTransition. |
TransitionCallback | type | () => void | Promise<void> |
TransitionHandle | type | Mirrors the native ViewTransition interface. |
Browser support
The View Transitions API is supported in recent Chromium-based browsers; see
caniuse for current coverage.
Everywhere else, vt-helper's fallback runs your callback with no animation, so your DOM updates
are consistent across all browsers — you only lose the transition effect, never the update itself.