View Transitions with useViewTransition
Dim includes a view transition system for sliding between UI states. The exported hook is useViewTransition (Storybook may label the section useTransition() — they refer to the same API).
There are two ways to use transitions:
- Declarative (auto) — set a
transitionIdprop on a component; Dim wraps the render in a two-layer slide host automatically. - Imperative (manual) — call
useViewTransition()inside your component for full control.
Declarative auto transitions
When a component receives a transitionId (via attribute or .props), Dim detects it during render and applies slide transitions whenever the id changes:
import { define, html, css, useState, useStyle } from "../core/dim.ts";
const PageHost = (props, { useState, useStyle, html, css }) => {
const [pageIndex, setPageIndex] = useState(0);
useStyle(css`
.page { padding: 1rem; min-height: 200px; }
.nav { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
`);
return html`
<div>
<nav class="nav">
<button @click="${() => setPageIndex(0)}">Home</button>
<button @click="${() => setPageIndex(1)}">About</button>
<button @click="${() => setPageIndex(2)}">Contact</button>
</nav>
<page-content
transitionId="${pageIndex}"
transitionDuration="400"
.props=${{ pageIndex }}
></page-content>
</div>
`;
};
const PageContent = (props, { html }) => {
const titles = ["Home", "About", "Contact"];
return html`<div class="page"><h2>${titles[props.pageIndex]}</h2></div>`;
};
define({ tag: "page-host", component: PageHost });
define({ tag: "page-content", component: PageContent });
Auto transition props
| Prop | Description |
|---|---|
transitionId | Any string or number; changes trigger a slide |
transitionDuration | Duration in ms (default 500) |
transitionAutoDirection | "false" disables numeric direction detection |
Direction is inferred automatically: higher numeric ids slide from the right, lower from the left.
Manual useViewTransition
For galleries, wizards, or custom animation logic, call the hook directly:
import {
define,
html,
css,
useState,
useStyle,
useViewTransition,
viewTransitionStyles,
} from "../core/dim.ts";
const Gallery = (props, { useState, useStyle, useViewTransition, html, css }) => {
const [index, setIndex] = useState(0);
const transition = useViewTransition(index.toString(), {
duration: 400,
autoDirection: true,
});
useStyle(css`
${viewTransitionStyles}
.slide-container { position: relative; overflow: hidden; }
.slide { padding: 2rem; }
`);
const goNext = () => {
if (!transition.isTransitioning) {
setIndex((i) => i + 1);
}
};
return html`
<div class="slide-container">
<div class="${transition.getIncomingClass('slide')}">
<h2>Slide ${index}</h2>
<button @click="${goNext}">Next</button>
</div>
</div>
`;
};
define({ tag: "my-gallery", component: Gallery });
Hook return value
| Property / method | Purpose |
|---|---|
isTransitioning | Whether a transition is in progress |
direction | 'left' or 'right' |
currentId / previousId | Last and previous transition ids |
startTransition(id, direction?) | Imperatively start a transition |
endTransition() | End the current transition early |
getIncomingClass(base?) | CSS class for incoming layer |
getOutgoingClass(base?) | CSS class for outgoing layer |
getTransitionStyles() | Inline style object for duration/easing |
options | Resolved options object |
Import viewTransitionStyles and inject it with useStyle to get the base slide CSS (.slide-in-right, .slide-out-left, etc.).
Two-layer rendering
Auto mode uses AutoTransitionHost internally:
- Incoming layer — current content slides in
- Outgoing layer — previous content slides out in the opposite direction
- Wrapper height animation — container height animates to match new content
Custom elements default to display: inline; transition hosts force block layout so layers are visible. If slides appear invisible, check that the host has block display and non-zero height.
Navigation stacks
Multi-screen apps combine transitionId with a navigation stack. See 10. Building a Multi-Screen App and 7. Shared-Element Transitions for FLIP animations on top of slides.
Live examples: Dim Storybook — View Transitions.
Conclusion
useViewTransition and auto transitionId give Dim a distinctive navigation story without a separate router package. Use declarative ids for page hosts and the manual hook when you need fine-grained control over timing and CSS classes.