# Onboarding pager

> A full-screen onboarding pager: pages follow the finger 1:1 with parallax on their content, the page dot stretches toward the next page like a worm, and the Next button grows with the swipe into a full-width Get started on the last page.

- Type: Component (navigation)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/page-dots-onboarding
- Markdown: https://goodui.dev/native/components/page-dots-onboarding.md
- Registry item: https://goodui.dev/native/r/page-dots-onboarding.json
- Dependencies: react-native-gesture-handler, react-native-reanimated, react-native-svg
- Keywords: react native onboarding, onboarding screens, intro slider, walkthrough pager, page indicator dots, welcome screens, expo onboarding swiper

## When to use

- Two to six welcome pages before sign-up or on first launch: what the app does, why it matters, permissions to come.
- A short 'what's new' sequence after a big update.

## When not to use

- Pointing at controls on a real screen: use coach-mark.
- Browsing content cards inside a screen: use carousel.
- Switching between views of the same content: use segmented-control or tab-bar.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add https://goodui.dev/native/r/page-dots-onboarding.json
npx expo install --fix
```

The component lands in `components/goodui-native/page-dots-onboarding/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs locale.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
import { OnboardingPager } from "@/components/goodui-native/page-dots-onboarding/page-dots-onboarding";

export function Welcome({ finish }: { finish: () => Promise<void> }) {
  return (
    <OnboardingPager
      onDone={finish}
      pages={[
        { key: "book", title: "Book in two taps", description: "See open slots and confirm without a phone call." },
        { key: "remind", title: "Never miss a visit", description: "A reminder the day before and an hour before." },
        { key: "pay", title: "Pay when you leave", description: "Your card stays on file. Tips are optional." },
      ]}
    />
  );
}
```

## Examples

### Own layout with parallax layers

```tsx
{
  key: "stats",
  title: "Track your progress",
  content: (
    <View style={{ flex: 1, justifyContent: "center", padding: 24, gap: 16 }}>
      <OnboardingParallax depth={-0.4} scale><StatsArt /></OnboardingParallax>
      <OnboardingParallax depth={0.2}><Text size="title1" weight="medium">Track your progress</Text></OnboardingParallax>
    </View>
  ),
}
```

### Skip straight out, localized

```tsx
<OnboardingPager pages={pages} onDone={finish} onSkip={finish} labels={{ next: "Siguiente", done: "Empezar", skip: "Omitir" }} />
```

## API reference

### OnboardingPager

`import { OnboardingPager } from "@/components/goodui-native/page-dots-onboarding/page-dots-onboarding";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `pages` (required) | `OnboardingPage[]` | – | The pages, in order. Built for two to six. |
| `onDone` (required) | `() => unknown` | – | Runs when the last page's button is pressed. Return a promise and the button shows a spinner until it settles. |
| `index` | `number` | – | Controlled page index. Pair with `onIndexChange`. |
| `defaultIndex` | `number` | `0` | Uncontrolled first page. Default 0. |
| `onIndexChange` | `(index: number) => void` | – | Called when a different page settles. |
| `skippable` | `boolean` | `true` | Show the Skip button at the top until the last page. Default `true`. |
| `onSkip` | `() => void` | – | Called by Skip. Defaults to moving to the last page. |
| `labels` | `Partial<OnboardingPagerLabels>` | – | Overrides for the built-in strings (buttons and screen reader text); a GooduiLocaleProvider pack sets them app-wide. |
| `parallax` | `number` | `1` | Strength of the parallax on page content, 0 to 2. Default 1. |
| `haptic` | `boolean` | `true` | Select haptic per page, tap on the buttons. Default `true`. |
| `accessibilityLabel` | `string` | – | Name of the pager for screen readers. Default "Onboarding". Same as `labels.pager`. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

### OnboardingParallax

`import { OnboardingParallax } from "@/components/goodui-native/page-dots-onboarding/page-dots-onboarding";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `depth` | `number` | `0.2` | How far this layer moves on top of the page as a fraction of the page width. Negative lags (background), positive leads. Default 0.2. |
| `fade` | `boolean` | `true` | Fade the layer as its page leaves the center. Default `true`. |
| `scale` | `boolean` | `false` | Shrink the layer a little as its page leaves the center. Default `false`. |
| `children` (required) | `ReactNode` | – | – |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |

## Accessibility

- The page dots are one adjustable element: swipe up or down on them with VoiceOver or TalkBack to change pages; its value reads 'Page N of M', the title and the description. The pages themselves stay readable, so buttons inside custom pages are reachable.
- On the web the dots are a focusable slider with aria-valuenow, min, max and valuetext; the left and right arrow keys change pages. The pages are not inside it, so their own buttons are never nested in the slider.
- Only the current page is reachable: pages off screen are inert on the web and hidden from VoiceOver and TalkBack on native, so Tab and swipe navigation never land on an invisible page.
- Page changes are announced. The Next button is labelled Next or Get started (the hidden label is aria-hidden), busy while onDone's promise is pending.
- Skip is a button that disappears from screen readers on the last page. Each dot is a tap target for touch and the mouse; screen readers and the keyboard use the slider.

## Motion

- Pages follow the finger 1:1 with rubber-band resistance past the ends and settle on the smooth spring with the release velocity, at most one page per swipe.
- Parallax: the default illustration lags the page (depth −0.3) and shrinks a little, the title leads slightly (0.12) and the description leads more (0.32), so text arrives staggered; all fade with distance from the center.
- Page dots: the accent worm's leading edge reaches the next dot in the first half of the swipe and its trailing edge catches up in the second half.
- The Next button's width follows the swipe from compact to the full footer width between the last two pages, while Next slides out and Get started slides in and the dots and Skip fade away.
- Reduced motion: buttons and dots jump straight to the page and the layers stay still (they only fade); dragging still follows the finger.

## Haptics

- select when a different page settles, tap on Next, Get started, Skip; error if onDone's promise rejects. haptic={false} turns them off.

## Performance

- One shared position drives the track, every parallax layer, the worm and the button on the UI thread; nothing re-renders while swiping.
- All pages stay mounted; keep illustrations light (svg or optimized images).

## Notes for AI

- Give it a full-screen container (flex: 1); it lays out Skip at the top, the pages, then dots and the button at the bottom.
- onDone runs on the last page's button; return a promise (for example saving a 'seen onboarding' flag) to show a spinner.
- Without onSkip, Skip moves to the last page; pass onSkip={finish} to leave right away.
- Use content for a custom page and wrap layers in OnboardingParallax; depth is a fraction of the page width (negative lags).

## Related

- [Carousel](https://goodui.dev/native/components/carousel.md): A snapping card carousel that follows the finger 1:1, shrinks and dims the neighbours, stretches its pagination pill with the drag, and turns that pill into a timer when it autoplays.
- [Coach mark](https://goodui.dev/native/components/coach-mark.md): An onboarding spotlight: the screen dims around a cut-out over the measured target, and Next morphs the hole to the next target, changing shape and size in flight, while the tooltip glides alongside and a ring pulses on arrival.
- [Button](https://goodui.dev/native/components/button.md): A press-responsive button with haptics that runs async actions on its own: it gathers into a spinner, then opens into a success or error state.

## Guidance for AI tools

Onboarding pager: A full-screen onboarding pager: pages follow the finger 1:1 with parallax on their content, the page dot stretches toward the next page like a worm, and the Next button grows with the swipe into a full-width Get started on the last page. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
