Animated spotlight
A Reanimated cutout glides between targets and reshapes - rectangle, rounded, circle, pill - entirely on the UI thread.
Product tours, coachmarks, and onboarding for React Native - built for the New Architecture, where the old libraries broke.
MIT licensed · verified on iOS & Android · runs in Expo Go
And nothing that breaks on the New Architecture.
A Reanimated cutout glides between targets and reshapes - rectangle, rounded, circle, pill - entirely on the UI thread.
Flip above or below, shift to stay on-screen, and clear the notch and home indicator. Safe-area aware, per-step placement.
Off-screen targets scroll into view before they are highlighted - in a ScrollView, or a virtualized FlatList row via scrollToIndex.
Built-in light and dark, a colorScheme prop, tokens for color, radius, fonts, and labels - or replace the tooltip entirely.
Let people tap through the hole to use the real element. Configurable scrim taps, and the keyboard dismisses on every step.
showOnce tours fire on first launch and never nag again, through a pluggable storage adapter - AsyncStorage, MMKV, anything.
The established libraries were built for the old architecture and stopped working when Fabric became the default. Guideway started there.
| Capability | Guideway | copilot | tourguide | spotlight-tour |
|---|---|---|---|---|
| Works on the New Architecture (Fabric) | Yes | - | - | - |
| Animated, reshaping spotlight | Yes | - | Yes | Yes |
| Auto-scroll to off-screen targets | Yes | - | - | - |
| FlatList / virtualized lists | Yes | - | - | - |
| Light / dark theming built in | Yes | - | - | - |
| Show-once persistence | Yes | - | - | - |
| Hook-first, no wrapper views | Yes | - | - | Yes |
Shortened: react-native-copilot, rn-tourguide, react-native-spotlight-tour.
Mark a view with a ref, describe the tour as data, wrap the app. No HOCs, no wrapper views that shift your layout.
npx expo install guideway react-native-reanimated react-native-svgimport { TourProvider, useTour, useTourTarget } from 'guideway';
function Screen() {
const search = useTourTarget('search');
const { start } = useTour();
return (
<View>
<TextInput ref={search} placeholder="Search" />
<Button title="Show me around" onPress={() => start('main')} />
</View>
);
}
const tours = [{
id: 'main',
steps: [{ id: 'search', title: 'Find anything', body: 'Search here.' }],
}];
<TourProvider tours={tours}>
<Screen />
</TourProvider>