Skip to content
Native Animation
Comment Thread Expand animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add comment-thread-expand
Skia

Comment Thread Expand

An expanding comment tree with drawn connectors and rolling likes, with configurable comments, labels, fonts, and connector color.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

An expanding comment tree with drawn connectors and rolling likes, with configurable comments, labels, fonts, and connector color.

Why this one

Replies emerge from curved branches instead of appearing all at once. A fine six-particle heart celebration and rolling counter give a small action a satisfying finish. The Lumen demo pairs warm paper, original coffee artwork, and a real glass composer with a working reply flow.

How it works

  1. Parent and reply layouts are measured with onLayout, including each branch endpoint.
  2. One interruptible progress value per thread drives its measured height, Skia path trim, chevron rotation, and reply reveal. The opening spring uses damping 56 and stiffness 300 (effective mass 4); collapse takes 200 ms.
  3. Reply progress windows stagger the reveal. Each reply travels 8 points from the connector and scales from 0.96 without overshoot.
  4. One shared value per like animates the heart, six radial strokes, and two counter layers over 300 ms. Both counter layers fade by distance and rest at integer values.
  5. Gesture Handler owns taps and double taps. Press feedback has an independent value, and callbacks return to JavaScript through scheduleOnRN.
  6. Reduced motion removes travel, rotation celebration, and particles, revealing all replies with a shared fade.

Install

npx expo install @shopify/react-native-skia expo-image react-native-gesture-handler react-native-reanimated react-native-worklets

Copy this folder without demo/. Wrap your app in GestureHandlerRootView. Follow the installed Skia version's CanvasKit setup for web, and load this component after CanvasKit is ready (for example, with Skia's WithSkiaWeb boundary). The demo additionally uses expo-blur, expo-linear-gradient, and app-loaded Geist / Instrument Serif fonts. Icons: Lucide (ISC).

Usage

import CommentThreadExpand from './comment-thread-expand';

const comments = [{
  id: 'a', name: 'Taylor', initials: 'TA',
  text: 'A lovely way to start the day.', likes: 4,
  replies: [{ id: 'b', name: 'Sam', initials: 'SA', text: 'A little room to breathe.', likes: 1 }],
}];

export function Comments() {
  return <CommentThreadExpand comments={comments} />;
}
<CommentThreadExpand
  ref={threadRef}
  comments={comments}
  colorScheme="light"
  accentColor="#F43F5E"
  springDamping={56}
  springStiffness={300}
  fontFamily="YourBodyFont"
  mediumFontFamily="YourMediumFont"
  monoFontFamily="YourNumberFont"
  onReply={comment => openComposer(comment.id)}
  onLikeChange={(id, liked) => saveLike(id, liked)}
/>

CommentThreadExpandRef exposes setExpanded(id, expanded), setLiked(id, liked), and reset(). Autoplay belongs to the demo, which expands at 450 ms and likes the first reply at 1050 ms, looping every 5100 ms. The list supports one level of replies; IDs must be unique. Like and expansion states are local, with callbacks for persistence. Change a row's ID to initialize it as a new comment.

Props

PropTypeDefaultPurpose
commentsreadonly ThreadComment[]requiredNames, initials, text, timestamps, counts, tints, and replies
fontFamilystringsystemBody text
mediumFontFamilystringsystemNames and actions
monoFontFamilystringsystemTabular rolling count
accentColorstring#F43F5EHeart and particles
lineColorstring#D6CEC2 / #544B42Curved connector stroke
colorSchemelight / dark / autoautoText and connector palette
springDampingnumber56Opening damping, bounded to ζ ≥ 0.8 with effective mass 4
springStiffnessnumber300Opening response
onExpandedChange(id, expanded) => void—Expansion callback
onLikeChange(id, liked) => void—Like callback
onReply(comment) => void—Composer integration
styleStyleProp<ViewStyle>—Scroll container style
testIDstring—Test identifier
replyLabelstringundefinedLabel of each comment's reply action.
commentsreadonly ThreadComment[]RequiredComments and their replies.
fontFamilystringundefinedBody font; defaults to the system font.
mediumFontFamilystringundefinedName and action font.
monoFontFamilystringundefinedRolling number font.
accentColorstringundefinedHeart and active action color.
lineColorstringundefinedThread connector color.
colorScheme'light' | 'dark' | 'auto'undefinedTheme, following the device by default.
springDampingnumberundefinedOpening spring damping, bounded to a damping ratio of at least 0.8.
springStiffnessnumberundefinedOpening spring stiffness.
onExpandedChange( id: string, expanded: boolean, ) => voidundefinedCalled when a thread changes state.
onLikeChange( id: string, liked: boolean, ) => voidundefinedCalled when a comment changes its like state.
onReply(comment: ThreadComment) => voidundefinedCalled by the Reply action.
styleStyleProp<ViewStyle>undefinedScroll container style.
testIDstringundefinedTest identifier.
hideRepliesLabelstringHide repliesAction copy for collapsing an expanded thread.
formatReplyCount(count: number) => stringView <count> repliesFormats the action copy for expanding a thread.

Compatibility

Expo SDK 58; React Native 0.88.0; Reanimated 4.7.1; Worklets 0.13.0; Skia 2.14.0. Dependencies are generated from component imports in piece.json.

The demo uses BlurTargetView and the SDK 58 Android blur API (Expo BlurView documentation). The composer reserves 112 points of scroll padding; the thumbnail is bundled, never fetched remotely.

Accessibility

Actions have button roles, descriptive labels, 44-point minimum targets, and screen-reader activation. Thread toggles expose expanded; hearts expose selected. Collapsed replies are removed from accessibility and pointer interaction. Double tap is an optional like shortcut; the labeled heart is the equivalent single-tap action. Reduced motion uses a 180 ms simultaneous reply fade, without particles or reply transforms. Use the Reply callback to focus your own composer.

Implementation notes

Text wraps naturally without fixed-height comment rows. The 320 × 694 and 390 × 844 layouts reserve the top-right viewer close area; the article has a flexible text column and comments scroll rather than crowd the bottom composer. At 320 points, the first reply is visible before the 1050 ms autoplay like. No effect is created with an opaque block over content: the connector is a trimmed stroke, the burst is six strokes, and the composer uses actual blur.