# React Onboarding Flow: Slides, Berechtigungen, Installation

> React Onboarding Flow in Next.js: Slides mit Skip, Push- und Standortabfragen, die man ablehnen kann, gespeicherter Fortschritt, am Ende die PWA-Installation.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 16. Okt. 2026
- Kategorie: [Praxis: Screens und Apps](https://wingo-ui.com/de/blog/category/build)
- Lesezeit: 10 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/react-onboarding-flow
- Englische Fassung: https://wingo-ui.com/blog/react-onboarding-flow

## Kurz gesagt

Ein guter React Onboarding Flow hält die Willkommens-Slides kurz, mit gut sichtbarem „Skip“, und fragt danach auf einer eigenen Karte mit Begründung nach Benachrichtigungen und Standort. Der Browser-Dialog startet erst, wenn jemand auf „Allow“ tippt, so verbraucht „Not now“ nie die eine Anfrage, die eine blockierte Website nicht zurückbekommt. Speichere den Fortschritt laufend, halte den Abschluss auf dem Server fest und biete die PWA-Installation erst an, wenn der Flow fertig ist.

Ein React Onboarding Flow hat drei Aufgaben: in wenigen Sekunden zeigen, was die App kann, die Berechtigungen holen, die die App wirklich braucht, und genug über die Person speichern, damit der erste Screen nützlich ist. Viele Apps scheitern beim ersten Start an der zweiten Aufgabe. Sie öffnen den Browser-Dialog für Benachrichtigungen, sobald die Seite lädt, die Leute tippen auf „Blockieren“, und die Website kann nie wieder fragen. Dieses Tutorial baut in Next.js einen Flow mit Willkommens-Slides, die man überspringen kann. Er fragt auf einem eigenen Screen nach Benachrichtigungen und Standort und nennt für beides einen Grund. Nach einem Reload macht er an derselben Stelle weiter, und am Ende bietet er die PWA-Installation an.

## Wonach sollte eine App beim ersten Start fragen, und wann?

So wenig wie möglich, und jede Frage in dem Moment, in dem sie Sinn ergibt. Die [Analyse der Nielsen Norman Group zum Onboarding mobiler Apps](https://www.nngroup.com/articles/mobile-app-onboarding/) (Juni 2020) empfiehlt, auf Onboarding zu verzichten, wenn sich die Oberfläche selbst erklärt. Sie rät von Tutorials im Kartenstapel-Stil ab, und wenn du doch eins behältst, braucht es einen gut sichtbaren Skip-Button. Außerdem sollst du erklären, warum du Daten haben willst und wie du sie nutzt.

Die Regeln für Berechtigungen setzen die Browser. Stand Oktober 2026:

- Der [Permission-UX-Guide von web.dev](https://web.dev/articles/push-notifications-permissions-ux) (zuletzt aktualisiert im März 2025) rät davon ab, nach der Push-Berechtigung zu fragen, sobald jemand die Website öffnet. Er empfiehlt ein „Double Permission“-Muster, bei dem dein eigener Dialog zuerst kommt, und warnt, dass die App nach einer Blockierung nicht noch einmal fragen kann.
- Mit Firefox 72 funktioniert `Notification.requestPermission()` nur noch als Antwort auf eine Nutzeraktion wie einen Klick, sonst wird das Promise abgelehnt ([Mozilla Hacks](https://hacks.mozilla.org/2019/11/upcoming-notification-permission-changes-in-firefox-72/)).
- Chrome 80 hat eine [leisere Oberfläche für die Benachrichtigungs-Berechtigung](https://blog.chromium.org/2020/01/introducing-quieter-permission-ui-for.html) eingeführt (Januar 2020). Websites mit sehr niedriger Zustimmungsrate landen automatisch darin, bis ihre Raten besser werden.
- Der [Guide von web.dev zum Nutzerstandort](https://web.dev/articles/user-location) sagt, dass Leute Websites misstrauen, die beim Laden der Seite nach ihrer Position fragen, und dass die Anfrage auf eine Nutzeraktion folgen sollte.

Gute UX bei der Berechtigung für Push-Benachrichtigungen läuft also auf ein Muster hinaus: Deine eigene Karte sagt, wofür die Berechtigung gut ist. „Allow“ ruft die Browser-API aus genau diesem Klick auf, und „Not now“ speichert ein Überspringen, ohne den Browser anzufassen. Der Browser-Dialog bleibt unbenutzt, und du kannst ihn später im passenden Kontext anbieten.

## Wie baust du den React Onboarding Flow?

Starte mit dem Block [Onboarding Flow](https://wingo-ui.com/components/onboarding-flow). Er hat fünf Schritte: Willkommens-Slides, Berechtigungen, ein kurzes Profil, Einstellungen und einen Abschluss-Screen. Jede Antwort wird sofort gespeichert: Schließt jemand den Tab, geht es beim nächsten Öffnen im selben Schritt weiter.

```bash
npx wingo-ui@latest add onboarding-flow install-prompt
```

Der Block bringt das [Onboarding Carousel](https://wingo-ui.com/components/onboarding-carousel) mit, das er für die Slides nutzt, dazu Button, Switch, Combobox und die anderen Teile, die er importiert.

### Sperr die App hinter einem Abschluss-Flag

Entscheide auf dem Server, ob jemand den ersten Start schon hinter sich hat. Ein Flag nur in localStorage zeigt die Slides auf einem neuen Smartphone oder nach dem Löschen der Website-Daten noch einmal. Speicher deshalb `onboardedAt` am User und leite aus einem Layout um. Leg `/welcome` außerhalb der Route Group ab, die dorthin umleitet, sonst läuft die Umleitung im Kreis.

```tsx
// app/(app)/layout.tsx
import type { ReactNode } from "react";
import { redirect } from "next/navigation";
import { getCurrentUser } from "@/lib/auth"; // dein Auth-Helper

export default async function AppLayout({ children }: { children: ReactNode }) {
  const user = await getCurrentUser();
  if (user && !user.onboardedAt) redirect("/welcome");
  return children;
}
```

```tsx
// app/welcome/page.tsx
import { redirect } from "next/navigation";
import { getCurrentUser } from "@/lib/auth";
import { WelcomeFlow } from "./welcome-flow";

export default async function WelcomePage() {
  const user = await getCurrentUser();
  if (!user) redirect("/sign-in");
  if (user.onboardedAt) redirect("/");
  return <WelcomeFlow userId={user.id} name={user.name} />;
}
```

### Render den Flow mit deinen eigenen Inhalten

Der Block kommt mit den Slides, Interessen und Labels seiner Demo Acme Market. Ersetz sie, bevor du live gehst. `labels` wird flach zusammengeführt: Jeder Schlüssel auf oberster Ebene, den du übergibst, ersetzt den Standardwert komplett. Wenn du `permissions` überschreibst, brauchst du also beide Einträge vollständig.

```tsx
// app/welcome/welcome-flow.tsx
"use client";

import { useRouter } from "next/navigation";
import {
  OnboardingFlow,
  type OnboardingInterest,
  type OnboardingSlide,
} from "@/components/blocks/onboarding-flow";

const slides: OnboardingSlide[] = [
  {
    id: "nearby",
    title: "Angebote in deiner Nähe zuerst",
    description: "Wir sortieren nach Entfernung, also steht das nächste Fahrrad oben.",
    image: "/onboarding/nearby.svg",
    imageAlt: "",
  },
  {
    id: "chat",
    title: "Antworte von überall",
    description: "Käufer und Verkäufer schreiben sich in der App, auf jedem Gerät.",
    image: "/onboarding/chat.svg",
    imageAlt: "",
  },
];

const interests: OnboardingInterest[] = [
  { id: "bikes", label: "Fahrräder" },
  { id: "furniture", label: "Möbel" },
  { id: "electronics", label: "Elektronik" },
  { id: "kids", label: "Kinder" },
  { id: "garden", label: "Garten" },
];

export function WelcomeFlow({ userId, name }: { userId: string; name: string }) {
  const router = useRouter();

  return (
    <OnboardingFlow
      user={{ name }}
      slides={slides}
      interests={interests}
      minInterests={2}
      permissions={["notifications", "location"]}
      persistKey={`onboarding:${userId}`}
      // das Beispiel-Seitenpanel zeigt auf /demo/*.jpg, was die Installation nicht kopiert
      art="none"
      onUpload={async (blob) => {
        const response = await fetch("/api/avatar", { method: "POST", body: blob });
        if (!response.ok) throw new Error("Upload fehlgeschlagen");
        return (await response.json()).url;
      }}
      labels={{
        appName: "Northwind Market",
        welcomeHeading: "Willkommen bei Northwind Market",
        permissions: {
          notifications: {
            title: "Benachrichtigungen",
            reason: "Erfahre sofort, wenn ein Käufer antwortet",
            unsupported: "Dieser Browser kann keine Benachrichtigungen anzeigen",
          },
          location: {
            title: "Standort",
            reason: "Sortiere Angebote nach der Entfernung zu dir",
            unsupported: "Dieser Browser teilt seinen Standort nicht",
          },
        },
      }}
      onComplete={async (result) => {
        // result: { profile, preferences, permissions }, jede Berechtigung granted, denied oder skipped
        const response = await fetch("/api/onboarding", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(result),
        });
        if (response.ok) router.replace("/");
      }}
    />
  );
}
```

Gibt `onComplete` ein Promise zurück, dreht sich der Button „Go to Home“, bis es erledigt ist. Dein Route Handler setzt `onboardedAt` und speichert die Antworten. Die Tipps auf dem Abschluss-Screen stehen in `labels.tips`, die Einleitungen der Schritte in Schlüsseln wie `permissionsDescription`. Schreib also auch diese Texte um.

Drei Stolperfallen vor dem Livegang:

- **Das Seitenpanel.** Ab 1024px zeigt der Block ein Bild neben der Karte. Eigene Slides ersetzen es nur im Willkommens-Schritt. Die späteren Schritte behalten die Beispielfotos (Pfade unter `/demo/`, die `add` nicht kopiert) und Marktplatz-Vignetten wie eine Benachrichtigung „Michael Reed replied“. Übergib `art="none"` wie oben, außer du ersetzt auch `labels.art` und die Labels dieser Vignetten.
- **Das Profilfoto.** Ohne `onUpload` tut der Standard-Handler 900 ms lang so, als würde er hochladen, und behält das zugeschnittene Foto als Data-URL in `profile.avatar`. Das JSON, das du per POST schickst, kann also ein ganzes Bild enthalten. Das `onUpload` oben schickt den Blob an deine eigene Route und gibt dessen URL zurück.
- **Die Stadt.** Die Stadtsuche im Profil-Schritt und die Stadt, die ein erlaubter Standort einträgt, kommen aus der rumänischen Städteliste des Blocks (`lib/ro-locations`). Für einen anderen Markt tauschst du diese Daten aus oder lässt die Stadt aus dem gespeicherten Profil weg.

## Wie baust du Willkommens-Slides, die man überspringen kann?

Halt sie kurz und lass die Leute schon auf der ersten Slide gehen. Für sich allein funktioniert das React Onboarding Carousel so: Ein Wischen oder „Next“ geht weiter, die Punkte springen direkt zu einer Slide, die Pfeiltasten links und rechts blättern per Tastatur, und „Skip“ steht auf jeder Slide außer der letzten oben in der Ecke.

> Live-Demo (Onboarding Carousel): Wisch durch die vier Slides oder tipp auf Continue. Skip springt zur letzten, und Get started beendet die Tour und zeigt die App dahinter. Probier sie unter [Onboarding Carousel](https://wingo-ui.com/components/onboarding-carousel) aus und installier sie mit `npx wingo-ui@latest add onboarding-carousel`.

Im Flow führt „Skip“ auf den Slides direkt zum Schritt mit den Berechtigungen. Nutzt du das Carousel allein, etwa in einer App ohne Anmeldung, übergib `persistKey`, damit ein Abschluss auf diesem Gerät gespeichert bleibt, und setz `skipTo="complete"`. Wer das Onboarding überspringen will, beendet die Tour mit „Skip“ dann sofort, statt auf der letzten Slide zu landen und noch einmal tippen zu müssen.

Häng es als Modal über den Home-Screen, nicht auf eine eigene Route:

```tsx
// components/first-run.tsx, gerendert von der Startseite
"use client";

import { OnboardingCarousel, type OnboardingSlide } from "@/components/ui/onboarding-carousel";

export function FirstRun({ slides }: { slides: OnboardingSlide[] }) {
  return (
    <OnboardingCarousel
      modal
      defaultOpen
      slides={slides}
      persistKey="onboarding-v1"
      skipTo="complete"
    />
  );
}
```

Das Modal wartet mit dem Öffnen, bis der Speicher gelesen ist, also sieht ein wiederkehrender Besucher keinen einzigen Frame davon. Auf dem Smartphone gleitet es im Vollbild mit Safe-Area-Padding hoch, auf dem Desktop öffnet es sich als zentrierte Karte, und Escape oder ein Klick daneben zählt als „Skip“. Die Inline-Layouts verhalten sich anders: Der Server rendert sie, bevor der Speicher gelesen ist. Ein wiederkehrender Besucher sieht die Slides also kurz, sie blenden aus, und `onComplete` läuft nicht noch einmal. Ein Carousel auf einer eigenen Route `/intro`, das in `onComplete` umleitet, lässt diesen Besucher auf einer leeren Seite zurück.

Um die Slides beim Testen wieder zu sehen, ruf `writePersisted("onboarding-v1", null)` aus `@/hooks/use-persisted-state` auf. Änderst du den Schlüssel zu `onboarding-v2`, sieht jeder einmal eine neue Tour.

## Wie fragst du nach Benachrichtigungen und Standort, ohne blockiert zu werden?

Gib jeder Berechtigung eine Karte mit Grund und ruf den Browser nur aus „Allow“ auf. Genau das macht der Schritt mit den Berechtigungen: Jede Karte zeigt einen Titel und einen Grund in einer Zeile. „Allow“ ruft beim Tippen `Notification.requestPermission()` oder `navigator.geolocation.getCurrentPosition()` auf, und „Not now“ markiert die Berechtigung als übersprungen. „Continue“ ist nie deaktiviert, also bleiben beide Berechtigungen freiwillig.

> Live-Demo (Onboarding Flow): Tipp auf Allow auf der Karte für Benachrichtigungen: Die Demo antwortet wie ein Browser, der zugestimmt hat. Tipp beim Standort auf Not now und dann auf Allow anyway, um es dir anders zu überlegen. Probier sie unter [Onboarding Flow](https://wingo-ui.com/components/onboarding-flow) aus und installier sie mit `npx wingo-ui@latest add onboarding-flow`.

Die Demo übergibt `simulatePermissions`, das nach 600 ms mit granted antwortet. In deiner App lässt du es weg, und die Karten zeigen, was der Browser gesagt hat. Eine Karte zeigt „Allowed“, „Denied“ mit einem Hinweis auf die Einstellungen, „Skipped“ mit einem Link „Allow anyway“ oder eine kurze Notiz, wenn der Browser die API nicht hat. Eine Berechtigung, die bei einem früheren Besuch erteilt wurde, erscheint beim Mounten als „Allowed“, und ein erlaubter Standort trägt die Stadt im Profil ein. `onComplete` bekommt jede Antwort als `granted`, `denied` oder `skipped`, also weißt du, wen du später noch einmal fragen kannst. Ein Browser ohne die API meldet `denied`. `skipped` deckt sowohl „Not now“ ab als auch einen Browser-Dialog, der ohne Antwort geschlossen wurde. In beiden Fällen darfst du später erneut fragen.

Zwei Entscheidungen liegen bei dir. Erstens: Lass eine Berechtigung weg, die die App noch nicht begründen kann. `permissions={["location"]}` fragt nur nach dem Standort, und `[]` überspringt den Schritt. Lohnen sich Benachrichtigungen erst, wenn jemand ein Angebot eingestellt hat, frag auch erst in diesem Moment. Zweitens: Auf iPhone und iPad funktioniert Web Push nur in der installierten App auf dem Home-Bildschirm ([WebKit, iOS 16.4](https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/)). Der Beitrag zum [React PWA Install Prompt](https://wingo-ui.com/de/blog/react-pwa-install-prompt) zeigt, wie du die Karte für Benachrichtigungen in einem Safari-Tab mit `useInstallPrompt()` weglässt.

## Wie fragst du später im Kontext nach Benachrichtigungen?

Mit demselben Muster außerhalb des Onboardings. Zeig eine Karte in dem Moment, in dem der Nutzen offensichtlich ist, etwa direkt nachdem jemand die erste Nachricht geschickt hat, und nur solange der Browser noch fragen kann:

```tsx
// components/reply-alerts-card.tsx
"use client";

import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";

export function ReplyAlertsCard({ onAnswer }: { onAnswer?: (answer: NotificationPermission) => void }) {
  const [canAsk, setCanAsk] = useState(false);
  const [asking, setAsking] = useState(false);

  useEffect(() => {
    // "default" heißt, der Browser hat noch nicht gefragt; granted und denied sind hier endgültig
    setCanAsk("Notification" in window && Notification.permission === "default");
  }, []);

  if (!canAsk) return null;

  async function allow() {
    setAsking(true);
    // aus dem Klick aufgerufen: Firefox lehnt es sonst ab
    const answer = await Notification.requestPermission();
    setAsking(false);
    setCanAsk(false);
    onAnswer?.(answer);
  }

  return (
    <div className="flex flex-col gap-3 rounded-2xl bg-card p-4 shadow-raised sm:flex-row sm:items-center">
      <p className="flex-1 text-sm text-muted-foreground">
        Willst du wissen, wann Emma antwortet? Wir benachrichtigen dich nur bei Nachrichten.
      </p>
      <div className="flex gap-2">
        <Button variant="ghost" size="sm" onClick={() => setCanAsk(false)}>
          Nicht jetzt
        </Button>
        <Button size="sm" loading={asking} onClick={allow}>
          Einschalten
        </Button>
      </div>
    </div>
  );
}
```

Der [Button](https://wingo-ui.com/components/button) behält seine Breite, während `loading` das Label gegen einen Spinner tauscht. Merk dir ein „Nicht jetzt“ in deinem eigenen Speicher, damit die Karte nicht auf jeder Seite wiederkommt.

## Was ändert sich bei Onboarding-Screens in einer PWA?

Die Screens bleiben dieselben, mit zwei Unterschieden: Push auf dem iPhone braucht die installierte App, und das Angebot zur Installation gehört hinter das Onboarding. Fürs PWA-Onboarding sieht das so aus:

| Onboarding-Screens | PWA (installiert) | Browser-Tab |
| --- | --- | --- |
| Willkommens-Slides | einmal, Vollbild mit Safe-Area-Padding | einmal, gleiches Layout |
| Karte für Benachrichtigungen | fragen | auf iPhone und iPad weglassen |
| Karte für Standort | fragen | fragen |
| Angebot zur Installation | nichts, sie ist schon installiert | nach dem Flow, auf der Startseite |

Häng den [Install Prompt](https://wingo-ui.com/components/install-prompt) einmal ins Root-Layout und blende ihn aus, solange der Flow läuft. Chrome löst `beforeinstallprompt` aus, sobald die Seite die Bedingungen erfüllt, oft während jemand noch auf den Slides ist, und ein Listener, der danach dazukommt, verpasst das Event. `useInstallPrompt()` startet den gemeinsamen Listener, hebt das Event auf und ruft `preventDefault()` darauf auf. So kann das Banner später trotzdem „Install“ anbieten:

```tsx
// components/app-install-prompt.tsx, gerendert von app/layout.tsx nach {children}
"use client";

import { usePathname } from "next/navigation";
import { InstallPrompt, useInstallPrompt } from "@/components/ui/install-prompt";

export function AppInstallPrompt() {
  // lauscht weiter auf das Install-Event, solange /welcome offen ist
  useInstallPrompt();
  const pathname = usePathname();
  if (pathname.startsWith("/welcome")) return null;

  return (
    <InstallPrompt
      appName="Northwind Market"
      appIcon="/icon-192.png"
      description="Öffne sie von deinem Home-Bildschirm, auch offline."
      delay={5000}
    />
  );
}
```

> Live-Demo (Install Prompt): Tipp auf Install: Die Demo spielt den Installationsdialog von Chrome ab, der Button zeigt einen Spinner, dann Installed mit einem Haken, und das Banner verschwindet. Probier sie unter [Install Prompt](https://wingo-ui.com/components/install-prompt) aus und installier sie mit `npx wingo-ui@latest add install-prompt`.

Wenn der Flow auf `/` umleitet, wartet das Banner `delay` Millisekunden und gleitet dann hoch. Die Leute haben die App gerade eingerichtet, und kein Formular konkurriert um den Platz. In der installierten App rendert es nichts, und „Not now“ blendet es standardmäßig für 14 Tage aus (`dismissDays`).

## Was solltest du als Nächstes bauen?

Installier den Block und ersetz seine Inhalte: `npx wingo-ui@latest add onboarding-flow install-prompt`. Onboarding Flow, Onboarding Carousel und Install Prompt gehören zu [Wingo UI Pro](https://wingo-ui.com/de/pricing); Button, Switch und Alert, die sie nutzen, sind kostenlos. Für ein längeres Formular nach der Registrierung erklärt der Guide zum [React Multi Step Form](https://wingo-ui.com/de/blog/react-multi-step-form) die Validierung pro Schritt und gespeicherte Entwürfe, und die Komponente [Steps](https://wingo-ui.com/components/steps) gibt ihm eine Fortschrittsanzeige als Kopfzeile. Die übrigen Screens der App findest du im [Tutorial zum Next.js Admin Dashboard](https://wingo-ui.com/de/blog/nextjs-admin-dashboard-tutorial) und in den anderen [Schritt-für-Schritt-Anleitungen](https://wingo-ui.com/de/blog/category/build).

## Komponenten in diesem Artikel

- [Onboarding Flow](https://wingo-ui.com/components/onboarding-flow) (Pro): A React onboarding flow for a marketplace app: welcome slides, notification and location asks, a short profile, preferences and a finish. Installation: `npx wingo-ui@latest add onboarding-flow`
- [Onboarding Carousel](https://wingo-ui.com/components/onboarding-carousel) (Pro): A React onboarding carousel of first-run slides for an app or PWA: swipe or tap Next, follow the dots, Skip any time, then Get started. Installation: `npx wingo-ui@latest add onboarding-carousel`
- [Install Prompt](https://wingo-ui.com/components/install-prompt) (Pro): A React PWA install prompt: the browser's install dialog in Chrome and Edge, and an Add to Home Screen guide on iPhone and Mac. Installation: `npx wingo-ui@latest add install-prompt`

## Häufige Fragen

### Wann sollte eine Web-App nach der Berechtigung für Benachrichtigungen fragen?

Wenn die Leute sehen, wofür die Benachrichtigungen gut sind, und immer nach einem Klick. web.dev rät davon ab, direkt beim Öffnen der Website zu fragen, und Firefox ab Version 72 lehnt Aufrufe von Notification.requestPermission() ab, die nicht von einer Nutzeraktion kommen.

### Kann ich erneut nach der Berechtigung fragen, nachdem jemand sie blockiert hat?

Nein. Sobald der Browser-Dialog mit „Blockieren“ beantwortet ist, bleibt Notification.permission auf denied, und nur die Person selbst kann das in den Website-Einstellungen des Browsers ändern. Deshalb kommt zuerst deine eigene Karte mit „Allow“ und „Not now“: „Not now“ auf deiner Karte lässt den Browser-Dialog unbenutzt.

### Wie zeige ich das Onboarding in React nur einmal?

Speicher den Abschluss. Auf einem Gerät reicht ein Flag in localStorage (das Onboarding Carousel macht das mit persistKey). Für angemeldete Nutzer speicherst du in onComplete ein Abschlussdatum am User-Datensatz und leitest auf dem Server um, wenn es fehlt, damit ein neues Smartphone die Slides nicht noch einmal zeigt.

### Sollten Onboarding-Slides übersprungen werden können?

Ja. Die Nielsen Norman Group rät bei den meisten Apps von Onboarding im Kartenstapel-Stil ab und sagt, dass jedes verbleibende einen gut sichtbaren Skip-Button braucht. Bleib bei höchstens drei Slides mit je einer Idee.

### Bekommen iPhone-Nutzer Web-Push-Benachrichtigungen von einer PWA?

Nur in der installierten App auf dem Home-Bildschirm. WebKit hat Web Push für Web-Apps auf dem Home-Bildschirm mit iOS 16.4 eingeführt. In einem Safari-Tab lässt du die Karte für Benachrichtigungen also aus dem Onboarding weg und fragst erst nach der Installation.

---

Quelle: https://wingo-ui.com/de/blog/react-onboarding-flow
