
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
A lightweight, flexible React Native library for creating powerful, step-by-step guided product tours with smart positioning and animations
A lightweight, flexible, and dependency-free library for creating powerful, step-by-step guided product tours for your React Native applications.
Inspired by the amazing Driver.js, this library lets you highlight UI components to guide your users, improve the onboarding process, and showcase new features in an elegant and simple way.
npm install rn-sherpa react-native-reanimated
# or
yarn add rn-sherpa react-native-reanimated
Add the Reanimated plugin to your babel.config.js:
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: ['react-native-reanimated/plugin'],
};
Note: The Reanimated plugin must be listed last in the plugins array.
Here's a simple example to get you started:
import React from 'react';
import { View, Text, TouchableOpacity, ScrollView } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import {
TourProvider,
TourOverlay,
useTour,
useStepRef,
useAutoScroll,
type TourConfig,
} from 'rn-sherpa';
function App() {
const tourConfig: TourConfig = {
steps: [
{
id: 'welcome',
title: 'Welcome! 🎉',
text: 'Let me show you around this app.',
popoverPosition: 'center',
},
{
id: 'header',
title: 'App Header',
text: 'This is your main navigation area.',
popoverPosition: 'bottom',
padding: 12,
borderRadius: 12,
},
{
id: 'button',
title: 'Action Button',
text: 'Tap this button to perform an action.',
popoverPosition: 'bottom',
},
],
showButtons: true,
showProgress: true,
allowClose: true,
animationDuration: 400,
overlayColor: 'rgba(0, 0, 0, 0.75)',
};
return (
<TourProvider config={tourConfig}>
<SafeAreaProvider>
<YourApp />
<TourOverlay config={tourConfig} />
</SafeAreaProvider>
</TourProvider>
);
}
function YourApp() {
const tour = useTour();
const scrollViewRef = React.useRef<ScrollView>(null);
// Create refs for tour steps
const headerRef = useStepRef<View>();
const buttonRef = useStepRef<View>();
// Automatically scroll to elements during tour
useAutoScroll(scrollViewRef);
// Assign refs to tour steps
React.useEffect(() => {
if (tour.currentStep) {
switch (tour.currentStep.id) {
case 'header':
tour.currentStep.ref = headerRef;
break;
case 'button':
tour.currentStep.ref = buttonRef;
break;
}
}
}, [tour.currentStep, headerRef, buttonRef]);
return (
<View style={{ flex: 1 }}>
<ScrollView ref={scrollViewRef}>
<View ref={headerRef} style={{ padding: 20, backgroundColor: '#007AFF' }}>
<Text style={{ color: 'white', fontSize: 24 }}>My App</Text>
</View>
<TouchableOpacity
onPress={tour.start}
style={{ padding: 16, backgroundColor: '#28a745', margin: 20 }}
>
<Text style={{ color: 'white' }}>Start Tour</Text>
</TouchableOpacity>
<TouchableOpacity
ref={buttonRef}
style={{ padding: 16, backgroundColor: '#007AFF', margin: 20 }}
>
<Text style={{ color: 'white' }}>Important Button</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
Wrap your app with TourProvider to enable tour functionality.
<TourProvider config={tourConfig}>
{/* Your app */}
</TourProvider>
Configuration object for the tour:
interface TourConfig {
// Required
steps: TourStep[];
// UI Options
showButtons?: boolean; // Show navigation buttons (default: true)
showProgress?: boolean; // Show "X of Y" progress indicator (default: true)
allowClose?: boolean; // Allow closing tour with X button (default: true)
// Animation
animationDuration?: number; // Animation duration in ms (default: 300)
// Styling
overlayColor?: string; // Overlay color (default: 'rgba(0, 0, 0, 0.75)')
popoverStyle?: ViewStyle; // Custom styles for popover
overlayStyle?: ViewStyle; // Custom styles for overlay
// Callbacks
onStart?: () => void; // Called when tour starts
onComplete?: () => void; // Called when tour completes normally
onSkip?: () => void; // Called when tour is closed/skipped
}
Configuration for individual tour steps:
interface TourStep {
id: string;
text: string;
title?: string;
ref?: React.RefObject<any>;
popoverContent?: ReactNode;
popoverPosition?: 'top' | 'bottom' | 'left' | 'right' | 'center';
onShow?: () => void;
onHide?: () => void;
padding?: number;
borderRadius?: number;
}
Access tour controls and state:
const {
currentStepIndex,
totalSteps,
isActive,
start,
stop,
next,
previous,
goToStep,
currentStep,
} = useTour();
Create refs for components to highlight:
const myComponentRef = useStepRef<View>();
<View ref={myComponentRef}>
{/* Component to highlight */}
</View>
Automatically scrolls to tour elements in ScrollView:
const scrollViewRef = useRef<ScrollView>(null);
// Basic usage
useAutoScroll(scrollViewRef);
// With options
useAutoScroll(scrollViewRef, {
topPadding: 150, // Padding from top (default: 100)
animated: true, // Animate scroll (default: true)
enabled: true, // Enable/disable (default: true)
});
<ScrollView ref={scrollViewRef}>
{/* Your content */}
</ScrollView>
The library automatically adjusts popover positions to ensure they're always visible on screen. When you specify a popoverPosition, the library:
Example:
{
id: 'bottom-button',
title: 'Settings',
text: 'Access your settings here.',
popoverPosition: 'bottom', // Will auto-switch to 'top' if near bottom of screen
}
How it works:
bottom but there's less than 240px space below → switches to toptop but there's less than 240px space above → switches to bottomleft ↔ rightFor scrollable content, you can implement auto-scrolling to ensure tour elements are visible:
function YourApp() {
const tour = useTour();
const scrollViewRef = React.useRef<ScrollView>(null);
// Auto-scroll when tour step changes
React.useEffect(() => {
if (tour.isActive && tour.currentStep?.ref?.current && scrollViewRef.current) {
const ref = tour.currentStep.ref.current;
ref.measureLayout(
scrollViewRef.current as any,
(_x: number, y: number, _width: number, height: number) => {
scrollViewRef.current?.scrollTo({
y: Math.max(0, y - 100), // 100px padding from top
animated: true,
});
},
(error) => console.log('Measurement failed:', error)
);
}
}, [tour.isActive, tour.currentStep, tour.currentStepIndex]);
return (
<ScrollView ref={scrollViewRef}>
{/* Your content */}
</ScrollView>
);
}
Best Practices:
y - 100) to avoid elements at screen edgesanimated: true for smooth scrolling transitionsCustomize the spotlight cutout around highlighted elements:
{
id: 'custom-spotlight',
title: 'Custom Highlight',
text: 'Notice the rounded corners and extra padding.',
padding: 16, // Extra space around element (default: 8)
borderRadius: 20, // Corner radius for spotlight (default: 8)
}
const tourConfig: TourConfig = {
steps: [
{
id: 'custom',
title: 'Custom Content',
text: 'This won\'t be shown',
popoverContent: (
<View>
<Text>Your custom JSX content here!</Text>
</View>
),
},
],
};
function MyComponent() {
const tour = useTour();
return (
<View>
<Button title="Start Tour" onPress={tour.start} />
<Button title="Stop Tour" onPress={tour.stop} />
<Button title="Next Step" onPress={tour.next} />
<Button title="Previous Step" onPress={tour.previous} />
<Button title="Go to Step 3" onPress={() => tour.goToStep(2)} />
</View>
);
}
const tourConfig: TourConfig = {
steps: [
{
id: 'step1',
title: 'Step 1',
text: 'First step',
onShow: () => console.log('Step 1 shown'),
onHide: () => console.log('Step 1 hidden'),
},
],
onStart: () => console.log('Tour started'),
onComplete: () => console.log('Tour completed'),
onSkip: () => console.log('Tour skipped'),
};
The library uses react-native-reanimated for smooth, performant animations:
const tourConfig: TourConfig = {
steps: [...],
animationDuration: 500, // Customize animation speed (default: 300ms)
};
Built-in Animations:
top position → Slides in from topbottom position → Slides in from bottomleft position → Slides in from leftright position → Slides in from rightcenter position → Fades inThe animations automatically adapt to the actual calculated position, so if smart positioning switches from bottom to top, the animation will also switch to match.
Solution: The library includes smart positioning that automatically adjusts placement. If this still happens:
setTimeout(() => tour.start(), 100);
Solution: Elements need to be measured before highlighting. The library includes automatic retry logic, but you can help by:
onShow callback to verify the element is ready:
{
id: 'step1',
title: 'Step 1',
text: 'Content',
onShow: () => console.log('Step showing - element should be rendered'),
}
Solution: Implement the auto-scroll pattern shown in the "Auto-Scroll to Elements" section above. The library handles measurement timing, but you need to provide the scroll logic.
Problem: Animations stuttering or not working
Solution:
react-native-reanimated is properly installedbabel.config.jspod install in the ios folderProblem: Type errors with refs or config
Solution:
import type { TourConfig, TourStep } from 'rn-sherpa';
useStepRef:
const myRef = useStepRef<View>();
Structure your tour steps from top to bottom, following the natural reading order:
const tourConfig: TourConfig = {
steps: [
{ id: 'welcome', popoverPosition: 'center' }, // Start with welcome
{ id: 'header', popoverPosition: 'bottom' }, // Then top elements
{ id: 'content', popoverPosition: 'top' }, // Middle elements
{ id: 'footer', popoverPosition: 'top' }, // Bottom elements
],
};
Choose positions that make sense for each element's location:
popoverPosition: 'bottom'popoverPosition: 'top'left or right based on screen positioncenter for full-screen focusThe smart positioning system will auto-adjust if needed!
Always implement scroll-to-view for scrollable content to ensure great UX:
React.useEffect(() => {
if (tour.isActive && tour.currentStep?.ref?.current && scrollViewRef.current) {
// Scroll implementation here
}
}, [tour.isActive, tour.currentStep, tour.currentStepIndex]);
Leverage callbacks for analytics, state management, or triggering actions:
const tourConfig: TourConfig = {
steps: [
{
id: 'feature',
title: 'New Feature',
text: 'Check this out!',
onShow: () => {
// Track step view
analytics.track('tour_step_viewed', { step: 'feature' });
},
},
],
onComplete: () => {
// Mark tour as completed
AsyncStorage.setItem('tour_completed', 'true');
},
};
Adjust padding and border radius based on the element type:
// Large padding for small buttons
{ id: 'small-icon', padding: 16, borderRadius: 20 }
// Minimal padding for large cards
{ id: 'card', padding: 8, borderRadius: 12 }
Always wrap your app with SafeAreaProvider to handle notches and safe areas correctly:
import { SafeAreaProvider } from 'react-native-safe-area-context';
<TourProvider config={tourConfig}>
<SafeAreaProvider>
<YourApp />
</SafeAreaProvider>
<TourOverlay config={tourConfig} />
</TourProvider>
The smart positioning system works across different screen sizes, but always test:
Check out the example app for a complete working demo.
MIT
Contributions are welcome! Please feel free to submit a Pull Request.
FAQs
A lightweight, flexible React Native library for creating powerful, step-by-step guided product tours with smart positioning and animations
The npm package rn-sherpa receives a total of 0 weekly downloads. As such, rn-sherpa popularity was classified as not popular.
We found that rn-sherpa demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.