Adapter details
The rest of the React adapter, after Getting Started: what
<Riffle> passes through, the riffleRef handle, how option changes reach the engine, refs, and
server components.
If you also import from the core directly, for example fan() to tune the layout, no extra
install is needed: both entries ship in the same package.
<Riffle>
Section titled “<Riffle>”Any prop that is not one of <Riffle>’s own (cards, children, getKey, onChange,
riffleRef, cardClassName) and not a Riffle option goes to the root element: id,
aria-label, data-* and so on, with style and className merged into what the adapter sets.
ref on <Riffle> reaches the root element, under React 18 and React 19 alike: the component is
wrapped in forwardRef and composes your ref with its own.
riffleRef
Section titled “riffleRef”riffleRef receives a RiffleInstance: next, prev, goTo, on, instance (the live
engine, or null while unmounted), activeIndex and state (the current snapshot). Reading
activeIndex or state from the ref does not re-render anything: use onChange for that, or
useRiffle with useRiffleState below.
Headless: useRiffle
Section titled “Headless: useRiffle”useRiffle(options) creates one engine per component instance and returns getRootProps and
getCardProps, a pair of prop getters that wire the engine to whatever markup you render, plus
the imperative handle (next, prev, goTo, subscribe, getSnapshot, on, instance).
Getting Started shows a
stack built this way.
Options between renders
Section titled “Options between renders”Options are compared with the previous render’s, and only what changed reaches the engine’s
update(). spring and rotation compare shallowly, so an inline object literal with the same
values is not a change. Every other option, layout and getLabel included, compares by
identity: pass a stable reference (module scope, useMemo, or a useState initializer) rather
than a fresh object or function on every render, or every render calls update(). Only two
changes rebuild the engine instead, keeping its position: a new axis, and cardWidth or
cardHeight switching between a number and 'auto'.
startIndex is read once, when the engine is first built. Changing it later does nothing: call
goTo to move.
getCardProps(index, props) wants a stable ref. If the ref you pass changes identity on every
render, React detaches and reattaches it, which unregisters and re-registers that card with the
engine every render. Pass a ref created once instead.
The root ref from getRootProps is safe to pass inline. Detaching it does not tear the engine
down at once: teardown waits one microtask, and a re-attach to the same element before then
cancels it. An inline ref callback, recreated every render, produces exactly that detach and
reattach within one commit, so it changes nothing. The same holds for React’s StrictMode, which
attaches, detaches and reattaches every ref at mount.
useRiffleState
Section titled “useRiffleState”useRiffleState subscribes to one slice of the snapshot instead of the whole thing, so a
component reading only activeIndex does not re-render when isDragging flips.
Server components
Section titled “Server components”The package build begins with 'use client', so frameworks with React Server Components treat it
as client code. Render <Riffle> from a client component of your own: its children is a
function, and a server component cannot pass one across.