<SectionFlow>
The root stage component. Groups consecutive <Section> children into transition spans (pinned viewports) and flow sections (native browser scroll).
import { SectionFlow, Section } from '@/components/sectionflow/core/section-flow';| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | required | <Section> elements. |
heightPerSection | number | 200 | Scroll distance (vh) for each transition's animation window. Longer = slower transition. |
restHeight | number | 100 | Global default for the viewing phase (vh) — reading window before animation begins. |
defaultTransition | TransitionComponent | string | — | Fallback transition for sections that omit one. |
className | string | — | Extra classes for the wrapper. |
<Section>
A marker component. Carries content and an optional outgoing transition declaration. Sections are mounted once as persistent layers when inside a transition span, or as plain <section> elements for native scroll.
<Section transition={CardStack}>
<div className="h-full w-full">Your content</div>
</Section>| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | required | Section content. |
transition | TransitionComponent | string | — | Outgoing transition — the handoff from this section to the next. Omit for native scroll. |
className | string | — | Extra classes on the section shell. |
TransitionProps
Every v2 transition component receives these props:
interface TransitionProps {
/** Local 0→1 progress across this edge's animation window. */
progress: MotionValue<number>;
/** Scroll direction across the edge. */
direction: TransitionDirection;
/** Pinned viewport size. */
viewport: Viewport;
/** Handle to the outgoing (current) layer. */
outgoing: LayerHandle;
/** Handle to the incoming (next) layer. */
incoming: LayerHandle;
}LayerHandle
A handle to a once-mounted section layer. Transitions write motion values into the style bag, which the stage spreads onto the real layer element:
interface LayerHandle {
/** Bag of MotionValues the transition writes. */
style: MotionStyle;
/** Measured bounds of the layer. */
bounds: LayerBounds;
/** Optional renderer for transitions that need real content clones. */
render?: () => ReactElement;
}TransitionComponent
A transition is a component that receives TransitionProps and optionally declares static timing and copies fields:
export function MyTransition({ progress, outgoing, incoming }: TransitionProps) {
const opacity = useTransform(progress, [0, 1], [1, 0]);
outgoing.style.opacity = opacity;
return null;
}
// Optional: override viewing-phase and animation-window duration (in vh)
MyTransition.timing = { rest: 120, duration: 250 };
// Optional: opt in to content cloning so the transition can render real copies
MyTransition.copies = true;The timing field lets a transition control how much scroll is allotted to its reading window (rest) and animation window (duration), overriding the stage's restHeight and heightPerSection defaults for that edge only. When copies is true, the stage exposes a render() method on each layer handle so the transition can produce real visual copies of the section content.
transitionRegistry
Maps string slugs to transition components:
import { transitionRegistry } from '@/components/sectionflow/core/registry';
// Use in Section:
<Section transition="card-stack">...</Section>
// Resolve programmatically:
const Component = transitionRegistry['card-stack'];resolveTransition(resolver)
Resolves a TransitionResolver (string slug or component) to a TransitionComponent | null:
import { resolveTransition } from '@/components/sectionflow/core/registry';
const component = resolveTransition('circular-portal'); // TransitionComponent | null
const component2 = resolveTransition(CircularPortal); // TransitionComponent | nullTransition shapes
V2 transitions fall into three common patterns:
Mask reveal
Writes a clip-path or mask onto the incoming layer:
const clipPath = useMotionTemplate`circle(${radius}% at 50% 50%)`;
incoming.style.clipPath = clipPath;Layer choreography
Writes transforms onto both layers directly:
outgoing.style.scale = outScale;
incoming.style.y = y;Effect overlay
Returns overlay elements (SVG, particles, shards) positioned above the layers:
return (
<>
<motion.div aria-hidden style={{ ... }} className="absolute inset-0 z-20">
{/* Overlay content */}
</motion.div>
</>
);Exported types
export type {
TransitionComponent,
TransitionResolver,
TransitionProps,
LayerHandle,
LayerBounds,
Viewport,
TransitionDirection,
TransitionTiming,
SectionProps,
SectionFlowProps,
};