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

@wrack/react-native-tour-guide

v1.0.1

Published

React Native tour guide, walkthrough, and coach marks library. Auto shape-matching spotlight, smart auto-scroll, themeable tooltips, pulse animation, and a declarative API. Works with Expo and React Native CLI — zero native dependencies, TypeScript, New A

Readme

React Native Tour Guide

A lightweight React Native library for building app tours, walkthroughs, and coach marks. The spotlight automatically matches your component's shape — circles stay circular, pills stay pill-shaped, no manual configuration needed.

Works with Expo and React Native CLI. Zero native dependencies. New Architecture ready.

npm version npm downloads license platforms PRs welcome good first issues


What does this library do?

It creates an overlay that highlights specific components in your app and shows tooltips explaining each one. You define a list of steps (which component to highlight, what to say), call startTour(), and the library handles everything else — measuring elements, positioning tooltips, scrolling to off-screen targets, animating between steps, and matching the spotlight shape to each component's border radius.

How is it different from other tour libraries?

| Capability | This library | Alternatives | |---|---|---| | Auto shape matching | Spotlight matches target's border radius automatically | Manual shape selection | | Auto-scroll | Scrolls to ensure target + tooltip both fit on screen | Manual or none | | Smart tooltip positioning | Auto-detects best position, never renders off-screen | Manual per step | | Theme presets | 4 built-in + createTheme() API | None | | Per-corner border radius | Extracted automatically from targetStyle | Not supported | | Pulse animation | Configurable animated spotlight border | Not supported | | Pause/Resume | Built-in state preservation | Not supported | | Tour persistence | Built-in "show only once" hook | DIY | | Conditional steps | active flag with auto-renumbering | Filter manually | | Bundle size | < 50KB, zero native dependencies | > 200KB |


Installation

# npm
npm install @wrack/react-native-tour-guide react-native-svg

# yarn
yarn add @wrack/react-native-tour-guide react-native-svg

# pnpm
pnpm add @wrack/react-native-tour-guide react-native-svg

# Expo (managed or bare)
npx expo install @wrack/react-native-tour-guide react-native-svg

Optional dependencies

These are only needed for enhanced visual effects:

# Blur effect
npm install @react-native-community/blur

# Gradient overlay
npm install react-native-linear-gradient

# Advanced blur masking
npm install @react-native-masked-view/masked-view

Quick start

1. Wrap your app

import { TourGuideProvider, TourGuideOverlay } from '@wrack/react-native-tour-guide';

export default function App() {
  return (
    <TourGuideProvider>
      <YourApp />
      <TourGuideOverlay />
    </TourGuideProvider>
  );
}

2. Start a tour

import { useRef } from 'react';
import { View, Text, Pressable } from 'react-native';
import { useTourGuide } from '@wrack/react-native-tour-guide';

function HomeScreen() {
  const { startTour } = useTourGuide();
  const buttonRef = useRef(null);
  const avatarRef = useRef(null);

  const handleStart = () => {
    startTour([
      {
        id: 'welcome',
        targetRef: buttonRef,
        title: 'Welcome',
        description: 'Tap here to get started.',
        targetStyle: styles.button, // spotlight matches button's border radius
      },
      {
        id: 'avatar',
        targetRef: avatarRef,
        title: 'Your Profile',
        description: 'Tap to view your profile.',
        targetStyle: styles.avatar, // circular target gets circular spotlight
      },
    ]);
  };

  return (
    <View>
      <Pressable ref={buttonRef} style={styles.button} onPress={handleStart}>
        <Text>Start Tour</Text>
      </Pressable>
      <View ref={avatarRef} style={styles.avatar} />
    </View>
  );
}

const styles = StyleSheet.create({
  button: { borderRadius: 12, padding: 16, backgroundColor: '#007AFF' },
  avatar: { width: 48, height: 48, borderRadius: 24, backgroundColor: '#ccc' },
});

That's it. The spotlight around the button will have 12px rounded corners. The spotlight around the avatar will be a perfect circle.


How does auto shape matching work?

Pass the same style you use on the component as targetStyle on the step. The library reads the borderRadius properties from that style and applies them to the spotlight.

  • Fully rounded elements (where borderRadius >= min(width, height) / 2) stay fully rounded even when the spotlight is slightly larger
  • Per-corner radii (borderTopLeftRadius, etc.) are preserved exactly — the spotlight matches the asymmetric shape
  • Partial radii (like borderRadius: 12 on a card) stay at exactly 12px on the spotlight
// Chat bubble with asymmetric corners
const chatStyle = {
  borderTopLeftRadius: 16,
  borderTopRightRadius: 16,
  borderBottomRightRadius: 16,
  borderBottomLeftRadius: 4,
};

{ id: 'chat', targetRef: chatRef, targetStyle: chatStyle, title: '...', description: '...' }
// Spotlight will have 16px on top-left, top-right, bottom-right and 4px on bottom-left

You can also override the border radius manually:

// Uniform override
{ id: 'step', targetRef: ref, spotlightBorderRadius: 20, title: '...', description: '...' }

Priority: spotlightBorderRadius > auto-extracted from targetStyle > default (12px).


How to enable auto-scroll?

Set scrollRef on the tour config. The library automatically scrolls to ensure both the target and its tooltip are fully visible.

const scrollViewRef = useRef(null);
const [scrollY, setScrollY] = useState(0);

startTour(steps, {
  scrollRef: scrollViewRef,
  getCurrentScrollOffset: () => scrollY,
});

// In your JSX:
<ScrollView
  ref={scrollViewRef}
  onScroll={(e) => setScrollY(e.nativeEvent.contentOffset.y)}
  scrollEventThrottle={16}
>
  {/* your content */}
</ScrollView>

You can also set scrollToTarget per step for fine-grained control:

{
  id: 'far-down',
  targetRef: ref,
  title: 'Scroll Target',
  description: '...',
  scrollToTarget: {
    scrollRef: scrollViewRef,
    offset: 50,
    animated: true,
    getCurrentScrollOffset: () => scrollY,
  },
}

How to use themes?

Four built-in themes are included. Spread them into your config:

import { darkTheme, lightTheme, minimalTheme, vibrantTheme } from '@wrack/react-native-tour-guide';

startTour(steps, { ...darkTheme });

Create a custom theme with createTheme():

import { createTheme } from '@wrack/react-native-tour-guide';

const brandTheme = createTheme({
  tooltipStyles: { primaryButtonColor: '#FF6B35', backgroundColor: '#1B1B3A' },
  spotlightStyles: { overlayOpacity: 0.7 },
});

startTour(steps, { ...brandTheme });

How to add pulse animation?

startTour(steps, {
  spotlightStyles: {
    enablePulse: true,
    pulseColor: '#00BFFF',
    pulseWidth: 3,
    pulseDuration: 1200,
    pulseMinOpacity: 0.3,
    pulseMaxOpacity: 0.9,
  },
});

How to show a tour only once?

Use the useTourPersistence hook with any storage backend:

import AsyncStorage from '@react-native-async-storage/async-storage';
import { useTourPersistence } from '@wrack/react-native-tour-guide';

function MyScreen() {
  const { startTour, resetTour } = useTourPersistence(AsyncStorage);

  useEffect(() => {
    // Automatically skips if user already completed this tour
    startTour(steps, { tourId: 'onboarding' });
  }, []);

  // Force show again
  const showAgain = async () => {
    await resetTour('onboarding');
    startTour(steps, { tourId: 'onboarding' }, true);
  };
}

Works with MMKV too:

import { MMKV } from 'react-native-mmkv';
const storage = new MMKV();

const adapter = {
  getItem: (key) => storage.getString(key) ?? null,
  setItem: (key, value) => storage.set(key, value),
  removeItem: (key) => storage.delete(key),
};

const { startTour } = useTourPersistence(adapter);

How to use conditional steps?

Set active: false to hide a step. Numbering adjusts automatically.

const isPremium = useIsPremium();

startTour([
  { id: 'welcome', targetRef: welcomeRef, title: 'Welcome', description: '...' },
  {
    id: 'upgrade',
    targetRef: upgradeRef,
    title: 'Upgrade',
    description: 'Only shown to free users.',
    active: !isPremium,
  },
  { id: 'done', targetRef: doneRef, title: 'All Set', description: '...' },
]);

How to handle backdrop taps?

startTour([
  {
    id: 'step1',
    targetRef: ref,
    title: 'Tap anywhere',
    description: 'Tapping the dark overlay advances to the next step.',
    backdropBehavior: 'next', // 'dismiss' | 'next' | 'none' | () => void
  },
]);

// Or set globally
startTour(steps, { defaultBackdropBehavior: 'next' });

How to pause and resume a tour?

const { pauseTour, resumeTour, isPaused } = useTourGuide();

// Pause when opening a modal
const handleModalOpen = () => pauseTour();

// Resume when it closes — picks up where it left off
const handleModalClose = () => resumeTour();

How to use a custom tooltip?

import type { TooltipProps } from '@wrack/react-native-tour-guide';

const MyTooltip = (props: TooltipProps) => (
  <View style={myStyles.tooltip}>
    <Text>{props.title}</Text>
    <Text>{props.description}</Text>
    <Pressable onPress={props.onNext}><Text>Continue</Text></Pressable>
  </View>
);

startTour(steps, {
  renderTooltip: (props) => <MyTooltip {...props} />,
});

Programmatic control

const {
  startTour, nextStep, prevStep, skipTour, endTour,
  goToStep, pauseTour, resumeTour,
  isActive, isPaused, currentStep, activeTourId,
} = useTourGuide();

API reference

TourStep

| Property | Type | Default | Description | |---|---|---|---| | id | string | required | Unique step identifier | | targetRef | RefObject | — | Ref to the component to highlight | | title | string | required | Tooltip title | | description | string | required | Tooltip body text | | tooltipPosition | 'top' \| 'bottom' \| 'left' \| 'right' \| 'auto' | 'auto' | Tooltip placement (auto-detected by default) | | targetStyle | ViewStyle | — | Style to extract border radius from for shape matching | | spotlightPadding | number | 0 | Extra padding around the spotlight | | spotlightBorderRadius | number | — | Override border radius (takes priority over targetStyle) | | scrollToTarget | ScrollToTargetConfig | — | Per-step scroll configuration | | active | boolean | true | Whether this step is included | | backdropBehavior | BackdropBehavior | 'none' | What happens on backdrop tap | | autoAdvance | number | 0 | Auto-advance after ms (0 = disabled) | | delayBefore | number | 0 | Delay before showing step | | onNext | () => void | — | Called on next | | onPrev | () => void | — | Called on previous | | onSkip | () => void | — | Called on skip | | onSpotlightPress | () => void | — | Called when spotlight area is tapped | | hideNextButton | boolean | false | Hide the next/done button | | hidePrevButton | boolean | false | Hide the back button | | hideSkipButton | boolean | false | Hide the skip button | | accessibilityLabel | string | — | Custom screen reader label |

TourGuideConfig

| Property | Type | Default | Description | |---|---|---|---| | tooltipStyles | TooltipStyles | — | Tooltip appearance | | spotlightStyles | SpotlightStyles | — | Spotlight/overlay appearance | | scrollRef | RefObject | — | ScrollView ref for auto-scrolling | | getCurrentScrollOffset | () => number | — | Returns current scroll Y position | | showProgressDots | boolean | false | Show dot indicators | | showStepCounter | boolean | true | Show "1/5" counter | | enableBackButton | boolean | true | Show back button | | nextButtonText | string | 'Next' | Next button label | | prevButtonText | string | 'Back' | Back button label | | skipButtonText | string | 'Skip' | Skip button label | | doneButtonText | string | 'Done' | Done button label | | animationDuration | number | 300 | Transition duration (ms) | | tooltipWidth | number | 320 | Tooltip width (px) | | tourId | string | — | Tour identifier (for persistence) | | autoPositionTooltip | boolean | — | Enable smart positioning | | defaultBackdropBehavior | BackdropBehavior | 'none' | Global backdrop behavior | | renderTooltip | (props) => ReactNode | — | Custom tooltip renderer | | onTourStart | () => void | — | Called when tour starts | | onTourEnd | (completed: boolean) => void | — | Called when tour ends | | onStepChange | (from, to) => void | — | Called on step change | | beforeStepChange | (from, to) => boolean \| Promise<boolean> | — | Gate before step change | | enableAccessibility | boolean | true | Enable screen reader announcements |

SpotlightStyles

| Property | Type | Default | Description | |---|---|---|---| | overlayOpacity | number | 0.6 | Overlay darkness (0-1) | | overlayColor | string | 'black' | Overlay color | | enableBlur | boolean | false | Blur effect (requires optional dep) | | blurAmount | number | 4 | Blur intensity | | enableGradient | boolean | false | Gradient overlay | | gradientColors | string[] | — | Gradient color stops | | enablePulse | boolean | false | Pulsing spotlight border | | pulseColor | string | '#FFFFFF' | Pulse color | | pulseWidth | number | 2 | Pulse border width (px) | | pulseDuration | number | 1500 | Full pulse cycle (ms) | | pulseMinOpacity | number | 0.2 | Pulse min opacity | | pulseMaxOpacity | number | 0.8 | Pulse max opacity |

TooltipStyles

| Property | Type | Default | Description | |---|---|---|---| | backgroundColor | string | '#2C2C2E' | Tooltip background | | borderRadius | number | 16 | Tooltip corner radius | | titleColor | string | '#FFFFFF' | Title text color | | descriptionColor | string | '#FFFFFF' | Description text color | | buttonTextColor | string | '#FFFFFF' | Button text color | | primaryButtonColor | string | '#007AFF' | Next/Done button background | | secondaryButtonColor | string | '#3A3A3C' | Back button background | | skipButtonColor | string | '#FFFFFF' | Skip button color |


Troubleshooting

Tour not showing?

  • Ensure TourGuideOverlay is placed after your main content inside the provider
  • Check that the target ref is attached to a mounted component
  • Add delayBefore: 500 if the component needs time to render

Spotlight position wrong?

  • The component must be visible on screen when measured
  • Check for transforms or absolute positioning that might affect measurement
  • Use scrollRef on the config to ensure off-screen elements are scrolled into view

Tooltip overlapping target?

  • Set tooltipPosition: 'auto' (default) for automatic placement
  • The library checks available space on all sides and picks the best position

Best practices

  1. Keep tours short — 3-7 steps for best engagement
  2. Always allow skipping — never trap users
  3. Use targetStyle — let the spotlight match automatically instead of manual configuration
  4. Set scrollRef globally — handles all scroll scenarios without per-step config
  5. Use tourId with useTourPersistence — show tours only once
  6. Use delayBefore instead of setTimeout — the library handles timing
  7. Use active for conditional steps — numbering adjusts automatically
  8. Test with VoiceOver/TalkBack — accessibility is enabled by default

Frequently asked questions

How do I add an onboarding tour to a React Native app?

Install @wrack/react-native-tour-guide and react-native-svg, wrap your app in TourGuideProvider with a TourGuideOverlay, then call startTour(steps) from the useTourGuide() hook. Each step references the component to highlight (via a ref) and the title/description to show. See Quick start.

How do I highlight a specific component with a spotlight?

Attach a ref to the component and pass it as targetRef on a step. The library measures the component, dims the rest of the screen, and cuts out a spotlight that matches the component's border radius automatically. Pass the component's style as targetStyle so the spotlight matches its shape.

Can I fully customize the tooltip?

Yes. Use config.tooltipStyles to restyle the built-in tooltip, or pass config.renderTooltip to render your own component — it receives the title, description, step index, and onNext/onPrev/onSkip handlers. See How to use a custom tooltip?.

Does it work with Expo?

Yes. It works with Expo (managed and bare) and React Native CLI. The only required dependency is react-native-svg, which Expo supports out of the box.

Does it support the New Architecture (Fabric)?

Yes. The dark overlay is drawn with a single even-odd SVG path (a real punched-out hole) rather than an SVG <Mask>, so the spotlight renders correctly on both the old architecture and Fabric, with no white film over the highlighted element.

Does it work with ScrollView, FlatList, and SectionList?

Yes. Pass a scrollRef (and a getCurrentScrollOffset getter) on the config and the tour scrolls off-screen targets into view automatically, keeping both the target and its tooltip on screen.

What are the dependencies and bundle size?

The library itself is under 50KB and has zero native dependencies — only react-native-svg as a peer. Blur and gradient effects are fully optional and load lazily only if you install them, degrading gracefully to the standard overlay otherwise.

Is it written in TypeScript?

Yes — it ships with full TypeScript types for every step option, config field, and theme.

How does it compare to react-native-copilot or rn-tourguide?

All three highlight UI elements with tooltips. This library additionally matches the spotlight shape to each target's border radius automatically (circles, pills, per-corner radii), auto-scrolls so the target and tooltip both fit on screen, and ships zero native dependencies — so it runs in Expo Go without a custom dev build.

Can I show a tour only once per user?

Yes — use the useTourPersistence hook with any storage backend (AsyncStorage, MMKV, or a custom adapter). See How to show a tour only once?.


Roadmap & help wanted

Contributions are welcome — issues and pull requests of any size help. Good places to start:

  • New features and enhancements
  • More built-in themes
  • Additional examples (drawer/tab navigators, modals, lists)
  • Documentation improvements and tests
  • Bug fixes of any size

Browse good first issues or open a discussion.


Contributing & community

Issues, ideas, and pull requests of every size are welcome — bug reports and docs improvements help just as much as features.

New to the project? Read CONTRIBUTING.md for local setup, the project structure, and the development workflow.

License

MIT — see LICENSE.


Created by Himanshu Lal