npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ladeologun/react-native-walkthrough

v0.1.2

Published

A shape-aware React Native walkthrough, product tour, and spotlight component for onboarding and feature discovery.

Readme

@ladeologun/react-native-walkthrough

A shape-aware React Native walkthrough component for onboarding, feature education, and spotlight flows.

It highlights a target, dims the rest of the screen, and shows a tooltip card that can follow the target across different layouts.

Demo

Watch the example app in action here:

https://github.com/user-attachments/assets/d8f12c02-f1c7-4bab-a2b6-05ffc70934b9

What You Get

  • target spotlight with animated highlight ring
  • optional smokey ripple halo for a more cinematic spotlight reveal
  • automatic tooltip placement above or below the target
  • support for controlled and imperative usage
  • optional scroll preparation before measurement
  • target measurement retries for unstable layouts
  • customizable text, styles, colors, spacing, and theme tokens
  • render hooks for fully custom header, content, and footer UI

Installation

npm install @ladeologun/react-native-walkthrough

Install the peer dependencies too:

npm install react-native-reanimated react-native-safe-area-context react-native-svg

Requirements

  • react-native-reanimated
  • react-native-safe-area-context
  • react-native-svg

Your app should already be set up correctly for Reanimated and Safe Area Context.

Basic Usage

import React, { useRef, useState } from 'react';
import { Pressable, Text, View } from 'react-native';
import {
  ScreenWalkthrough,
  type ScreenWalkthroughRef,
} from '@ladeologun/react-native-walkthrough';

export default function Example() {
  const buttonRef = useRef<View>(null);
  const walkthroughRef = useRef<ScreenWalkthroughRef>(null);
  const [visible, setVisible] = useState(true);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Pressable
        ref={buttonRef}
        style={{ padding: 16, borderRadius: 24, backgroundColor: '#0A9E4A' }}
      >
        <Text style={{ color: '#fff' }}>Tap me</Text>
      </Pressable>

      <ScreenWalkthrough
        ref={walkthroughRef}
        visible={visible}
        targetRef={buttonRef}
        contentTitle="Welcome"
        contentDesc="This is the action your users should learn first."
        nextBtnText="Done"
        hidePrevious
        onNext={() => setVisible(false)}
      />
    </View>
  );
}

Two Usage Patterns

Controlled

Use the visible prop when your app state decides when the walkthrough opens or closes.

<ScreenWalkthrough
  visible={visible}
  targetRef={targetRef}
  contentTitle="Profile"
  contentDesc="This is where users manage their account."
  onClose={() => setVisible(false)}
/>

Imperative

Use the ref methods when you want to open, close, or re-measure from code.

const walkthroughRef = useRef<ScreenWalkthroughRef>(null);

walkthroughRef.current?.open();
walkthroughRef.current?.refreshPosition();
walkthroughRef.current?.close();

How Targeting Works

You usually provide a targetRef that points to the element to highlight.

const targetRef = useRef<View>(null);

<Pressable ref={targetRef} collapsable={false} />

<ScreenWalkthrough targetRef={targetRef} />

Notes:

  • set collapsable={false} on native targets you want to measure reliably
  • targetRef should point to a host/native view that supports measureInWindow
  • if you pass children directly into ScreenWalkthrough, the wrapper can act as the measurement target when targetRef is omitted

Multi-Step Walkthroughs

This library does not manage step arrays for you. Instead, you control the current step in your own state and update:

  • targetRef
  • targetKey
  • contentTitle
  • contentDesc
  • nextBtnText
  • hidePrevious
  • callbacks like onNext and onPrevious

Use targetKey when the highlighted element changes between steps so the component knows it should re-measure.

Scroll-Then-Highlight Flows

If your target may be off-screen, use prepareTarget.

<ScreenWalkthrough
  targetRef={targetRef}
  prepareTarget={async () => {
    scrollRef.current?.scrollTo({ y: 500, animated: true });
  }}
  prepareTargetDelayMs={320}
/>

This is especially useful for:

  • ScrollView
  • FlatList
  • tabbed or collapsible layouts
  • screens where the target is created after user navigation

Exports

The library exports:

  • ScreenWalkthrough
  • defaultTheme
  • ScreenWalkthroughProps
  • ScreenWalkthroughRef
  • ScreenWalkthroughTarget
  • ScreenWalkthroughTheme
  • Placement
  • Rect

Ref Methods

ScreenWalkthroughRef

type ScreenWalkthroughRef = {
  open: () => void;
  close: () => void;
  refreshPosition: () => void;
  measureTarget: () => void;
};

What they do:

  • open(): opens the walkthrough imperatively
  • close(): closes the walkthrough imperatively
  • refreshPosition(): re-measures if the walkthrough is visible
  • measureTarget(): forces target measurement

Props Reference

Content and flow

| Prop | Type | Default | Description | | --- | --- | --- | --- | | visible | boolean | uncontrolled | Controls visibility when you use the component in controlled mode. | | contentTitle | string | undefined | Title shown in the tooltip card. | | contentDesc | string | undefined | Description shown in the tooltip card. | | nextBtnText | string | "Next" | Next button label. | | previousBtnText | string | "Previous" | Previous button label. | | hidePrevious | boolean | false | Hides the previous button and right-aligns the footer. | | onNext | () => void | undefined | Called when the next button is pressed. | | onPrevious | () => void | undefined | Called when the previous button is pressed. | | onClose | () => void | undefined | Called when the walkthrough closes. | | closeOnBackdropPress | boolean | true | Closes the walkthrough when the dimmed backdrop is tapped. |

Targeting and measurement

| Prop | Type | Default | Description | | --- | --- | --- | --- | | targetRef | RefObject<ScreenWalkthroughTarget \| null> | undefined | Ref to the view that should be highlighted. | | targetKey | string \| number \| null | undefined | Triggers re-measurement when the active target changes. Useful in multi-step flows. | | prepareTarget | () => void \| Promise<void> | undefined | Runs before measuring the target. Use it for scrolling or pre-layout work. | | prepareTargetDelayMs | number | 260 | Wait time after prepareTarget before measurement starts. | | measurementRetryDelayMs | number | 80 | Delay used between measurement retries. | | measurementRetryCount | number | 8 | Maximum number of measurement retries. | | targetPadding | number | 2 | Extra space added around the measured target. | | targetBorderRadius | number | 18 | Border radius used for the highlight cutout. | | targetOffsetX | number | 0 | Horizontal offset applied to the measured target. | | targetOffsetY | number | 0 | Vertical offset applied to the measured target. |

Placement and layout

| Prop | Type | Default | Description | | --- | --- | --- | --- | | placement | 'auto' \| 'top' \| 'bottom' | 'auto' | Tooltip placement. auto chooses the side with more room. | | width | number | 320 | Preferred tooltip width. | | minWidth | number | 280 | Minimum tooltip width after clamping. | | maxWidth | number | 360 | Maximum tooltip width after clamping. | | offset | number | 18 | Distance between target and tooltip card. | | edgePadding | number | 16 | Minimum spacing from screen edges. | | disableArrow | boolean | false | Hides the directional arrow. |

Appearance

| Prop | Type | Default | Description | | --- | --- | --- | --- | | backgroundColor | string | theme surface color | Background color of the tooltip card and arrow. | | overlayColor | string | theme overlay color | Dimmed screen overlay color. | | showPulse | boolean | true | Enables the inner pulse ring when ripple is enabled. | | showRipple | boolean | false | Master toggle for all ripple motion. When false, both the pulse ring and ripple halo are disabled. | | rippleColor | string | theme ripple color | Overrides the ripple halo and pulse color without affecting button/action text colors. | | theme | Partial<ScreenWalkthroughTheme> | defaultTheme | Design tokens for colors and typography. |

Render hooks

| Prop | Type | Description | | --- | --- | --- | | renderHeader | () => ReactNode | Replaces the header slot. | | renderContent | () => ReactNode | Replaces the default title/description content block. | | renderFooter | () => ReactNode | Replaces the default footer buttons. |

If you provide a render hook, that slot becomes fully yours.

Style hooks

| Prop | Applies To | | --- | --- | | parentViewStyle | Outer wrapper that contains walkthrough children. | | parentWrapperStyle | Additional wrapper styling around walkthrough children. | | childViewStyle | Inner wrapper around the rendered children. | | tooltipStyle | Tooltip card container. | | modalContentContainer | Extra style merged onto the tooltip card root. | | contentContainerStyle | Main content area inside the card. | | contentContainerHeaderStyle | Header slot container. | | contentContainerDescStyle | Content slot container. | | contentContainerBtnContainerStyle | Footer slot container. | | contentTitleTextStyle | Title text style. | | contentDescTextStyle | Description text style. | | nextBtnTextStyle | Next button text style. | | previousBtnTextStyle | Previous button text style. |

Theme Reference

ScreenWalkthroughTheme

type ScreenWalkthroughTheme = {
  colors: {
    overlay: string;
    surface: string;
    textPrimary: string;
    textSecondary: string;
    accent: string;
    white: string;
    shadow: string;
    highlight: string;
    ripple: string;
  };
  typography: {
    titleSize: number;
    titleLineHeight: number;
    titleWeight: '500' | '600' | '700';
    bodySize: number;
    bodyLineHeight: number;
    bodyWeight: '400' | '500';
    actionSize: number;
    actionLineHeight: number;
    actionWeight: '500' | '600' | '700';
  };
};

defaultTheme

const defaultTheme = {
  colors: {
    overlay: 'rgba(6, 15, 24, 0.68)',
    surface: '#FFFFFF',
    textPrimary: '#101828',
    textSecondary: '#667085',
    accent: '#0A9E4A',
    white: '#FFFFFF',
    shadow: '#000000',
    highlight: '#FFFFFF',
    ripple: 'rgba(255, 255, 255, 0.22)',
  },
  typography: {
    titleSize: 16,
    titleLineHeight: 24,
    titleWeight: '700',
    bodySize: 15,
    bodyLineHeight: 28,
    bodyWeight: '400',
    actionSize: 15,
    actionLineHeight: 22,
    actionWeight: '700',
  },
};

Example theme override

<ScreenWalkthrough
  targetRef={targetRef}
  contentTitle="Theme override"
  contentDesc="Use the theme prop to change colors and typography tokens."
  theme={{
    colors: {
      accent: '#D94841',
      surface: '#FFF8F6',
      ripple: 'rgba(217, 72, 65, 0.22)',
    },
    typography: {
      titleSize: 18,
      bodyLineHeight: 24,
    },
  }}
/>

Example ripple override

<ScreenWalkthrough
  targetRef={targetRef}
  showRipple
  rippleColor="rgba(34, 197, 94, 0.9)"
/>

Notes:

  • rippleColor only affects the ripple and pulse effect
  • action text still uses theme.colors.accent by default
  • you can also override button text directly with nextBtnTextStyle and previousBtnTextStyle

Full Example

import React, { useCallback, useRef, useState, type ElementRef } from 'react';
import { Pressable, ScrollView, Text, View } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import {
  ScreenWalkthrough,
  type ScreenWalkthroughRef,
} from '@ladeologun/react-native-walkthrough';

export default function Example() {
  const scrollRef = useRef<ElementRef<typeof ScrollView> | null>(null);
  const firstRef = useRef<View>(null);
  const secondRef = useRef<View>(null);
  const walkthroughRef = useRef<ScreenWalkthroughRef>(null);
  const [step, setStep] = useState<'first' | 'second' | null>('first');

  const targetRef = step === 'first' ? firstRef : secondRef;

  const prepareTarget = useCallback(async () => {
    if (step === 'second') {
      scrollRef.current?.scrollTo({ y: 500, animated: true });
    }
  }, [step]);

  return (
    <SafeAreaProvider>
      <ScrollView ref={scrollRef} contentContainerStyle={{ padding: 24 }}>
        <Pressable ref={firstRef} collapsable={false}>
          <Text>First target</Text>
        </Pressable>

        <View style={{ height: 600 }} />

        <Pressable ref={secondRef} collapsable={false}>
          <Text>Second target</Text>
        </Pressable>

        {step ? (
          <ScreenWalkthrough
            ref={walkthroughRef}
            visible
            targetRef={targetRef}
            targetKey={step}
            contentTitle={step === 'first' ? 'Step one' : 'Step two'}
            contentDesc="You control each step from your own state."
            nextBtnText={step === 'first' ? 'Next' : 'Done'}
            hidePrevious={step === 'first'}
            prepareTarget={prepareTarget}
            prepareTargetDelayMs={320}
            previousBtnText="Back"
            onPrevious={() => setStep('first')}
            onNext={() => setStep(step === 'first' ? 'second' : null)}
            onClose={() => setStep(null)}
          />
        ) : null}
      </ScrollView>
    </SafeAreaProvider>
  );
}

Tips

  • use targetKey for step changes
  • use prepareTarget when the target may be off-screen
  • call refreshPosition() after layout changes
  • pass collapsable={false} on target views for more reliable measurement
  • tune targetPadding, edgePadding, and offset to match your design

Troubleshooting

The wrong area is being highlighted

  • make sure targetRef points to a native host view
  • add collapsable={false} to the target
  • adjust targetOffsetX and targetOffsetY
  • call refreshPosition() after layout changes

The target is inside a scroll view

  • use prepareTarget
  • increase prepareTargetDelayMs if the scroll animation needs more time

The tooltip is too close to the screen edge

  • increase edgePadding
  • reduce width
  • lower minWidth

The tooltip is too close to the target

  • increase offset

Contributing

License

MIT


Made with create-react-native-library