useSkeletonHandoff

Keep a placeholder on screen through a fade while the real content arrives.

Returns a mounted flag and an opacity shared value for a loading placeholder. mounted stays true past the moment the data lands, for exactly as long as the fade takes, so the placeholder dissolves while the real content appears underneath it.

A placeholder cut at the frame loading turns false leaves a gap: it is gone, and whatever replaces it has not started drawing yet. Both states were meant to show something, and for a frame or two neither does.

Usage

import { useSkeletonHandoff } from 'panelui-native';
import Animated, { useAnimatedProps } from 'react-native-reanimated';
import { G } from 'react-native-svg';

const AnimatedG = Animated.createAnimatedComponent(G);

function ChartPlaceholder({ loading }: { loading: boolean }) {
  const { mounted, opacity } = useSkeletonHandoff(loading);
  const animatedProps = useAnimatedProps(() => ({ opacity: opacity.value }));

  if (!mounted) return null;

  return <AnimatedG animatedProps={animatedProps}>{/* the placeholder */}</AnimatedG>;
}

The fade runs on the UI thread. The unmount is scheduled back to JavaScript once, at the end, so nothing re-renders per frame.

For a view rather than an SVG node, read the same value with useAnimatedStyle:

const style = useAnimatedStyle(() => ({ opacity: opacity.value }));

Timing it against the reveal

The default 220ms suits a reveal of roughly 500–700ms: the placeholder is still faintly there while the first marks grow, and gone well before the last of them lands. Pass a duration to match a slower entrance.

const { mounted, opacity } = useSkeletonHandoff(loading, 320);

A fade longer than the reveal it covers is worse than a hard cut — the placeholder is still legible over content that has finished arriving, and the two read as two charts on top of each other.

Reduced motion

With the setting on, the placeholder is removed immediately rather than fading. It is the dissolve that is the motion, not the disappearance, so nothing is lost by skipping it.

API Reference

Parameters

ParameterTypeDefaultDescription
loadingbooleanTrue while the content is still on its way.
durationnumber220Milliseconds the placeholder takes to dissolve once it is not.

Returns

ValueTypeDescription
mountedbooleanWhether to render the placeholder at all — true through the fade, not only through the wait.
opacitySharedValue<number>1 while loading, tweened to 0 after. Read it in a worklet.

On this page