Native App Feeling für Web Apps: 12 React-Fixes

Du hast die App gebaut, sie zum Home-Bildschirm hinzugefügt, und sie fühlt sich trotzdem wie eine Website an: Links blitzen grau auf, wenn du tippst, ein Doppeltipp auf einen Button zoomt die Seite, das Menü öffnet sich als winziges Popover unter deinem Daumen, und die Tastatur verdeckt das Nachrichtenfeld. Native App Feeling entsteht aus einem Dutzend kleiner Verhaltensweisen, und die meisten davon kosten nur etwas CSS oder ein paar Props. Diese Liste geht zwölf davon durch, vom Start bis zur Tastatur, jeweils mit der Komponente, die wir dafür in Wingo UI gebaut haben, dem Code und dem, was ein Browser noch nicht kann.
Die Liste ist das praktische Gegenstück zu Mobile-first React-Komponenten, wo es um die Layout-Regeln dahinter geht. Weitere Guides findest du unter Mobile UI in React.
Wann fühlt sich eine Web App auf dem Handy nativ an?
Erst das Verhalten, dann die Optik. Die meisten Tipps dazu, wie sich eine PWA wie eine native App anfühlt, hören bei einem Manifest und ein paar Meta-Tags auf. Das ist Schritt eins von zwölf. Nutzer verzeihen einer Web App, dass sie nicht genau wie die Einstellungen auf dem iPhone aussieht. Sie merken aber sofort, wenn ein Tap wartet, eine Geste nichts tut oder die Tastatur verdeckt, was sie tippen. Wer Web Apps wie native Apps bauen will, behebt genau das zuerst. Deshalb beginnen die zwölf Details unten mit denen, die jede App braucht, und enden mit dem Feinschliff.
1. Startet sie wie eine App?
Eine installierte App öffnet sich ohne Adressleiste und füllt den Bildschirm. Dafür brauchst du ein Manifest mit display: "standalone" und einen Viewport, der bis an die Ränder reicht. Im Next.js App Router sind beides Dateien und Exports:
Mit viewportFit: "cover" rendert die Seite bis unter die Notch und den Home-Indikator. Jede Leiste, die an einem Rand fixiert ist, braucht also ihr eigenes Padding mit env(safe-area-inset-*). Unser Tailwind Safe Area Guide hat die Utilities und die Rechnung für gestapelte Leisten. Manche Details ergeben erst nach der Installation Sinn, etwa das Ausblenden deines eigenen Install-Banners. Mit einer Custom Variant bleibt das im CSS:
Dann blendet standalone:hidden ein Element in der installierten App aus. Der Guide zum React PWA Install Prompt zeigt, wie Nutzer die App in Chrome, Edge und auf dem iPhone installieren.
2. Reagiert ein Tap sofort?
Bei einem nativen Control zeigt sich der Druck direkt unter deinem Finger, ohne grauen Kasten und ohne Warten. Mit zwei CSS-Details schafft das auch ein Web-Button. touch-action: manipulation schaltet den Double-Tap-Zoom auf dem Element ab, damit ein schneller zweiter Tap auf einen Stepper nie die Seite zoomt. WebKit erklärt in seinem Beitrag zu schnellem Tippen (opens in a new tab) (Dezember 2015), dass einzelne Taps damit außerdem auf jeder Zoomstufe sofort ankommen. Und -webkit-tap-highlight-color: transparent entfernt den halbtransparenten Kasten, den Safari und Chrome über einen getippten Link legen. Das Preflight von Tailwind CSS v4 setzt ihn auf html schon auf transparent. In einer Tailwind-App ist die Aufgabe also die umgekehrte: Gib jedem drückbaren Element einen eigenen Pressed State.
Der kostenlose Button von Wingo UI trägt beide Klassen selbst. Ein kopierter Button behält sie also ohne globales Stylesheet und reagiert beim Drücken mit einer Spring statt einer CSS-Transition. Unser Tutorial zu Tailwind touch-manipulation geht jeden touch-*-Wert durch und zeigt, welchen Bug er jeweils behebt.
3. Zoomt die Seite, wenn ein Feld den Fokus bekommt?
Safari auf iOS zoomt in ein fokussiertes Feld, dessen Text kleiner als 16px ist, und zoomt nicht wieder heraus, wenn das Feld den Fokus verliert. maximum-scale=1 im Viewport stoppt diesen Zoom. Dann blockieren Android-Browser aber den Pinch-Zoom für die ganze Seite, und Lighthouse lässt die Seite in seinem Accessibility-Audit zum Viewport durchfallen. Nimm auf Handys 16px Text und ab md deine kleinere Größe:
Das Design-System von Wingo UI macht daraus eine Regel für jedes Textfeld, text-base md:text-sm, und die gemeinsame Field-Box und der Prompt Input halten sich daran. Achte auf denselben Bug an Stellen, die man leicht übersieht: Ein Suchfeld im Header, ein select und ein contenteditable-Editor zoomen alle die Seite, wenn ihre berechnete Schriftgröße unter 16px liegt.
4. Verhält sich die Oberfläche wie ein Dokument?
Nur dort, wo sie es soll. Eine Webseite lässt dich jeden Text markieren und reicht einen Scroll aus einer inneren Liste an die Seite dahinter weiter. Eine App tut beides bei ihren Controls nicht. Setz select-none auf Buttons, Tabs und Leisten, damit ein langer Druck auf einen Tab nicht sein Label markiert. Echter Inhalt bleibt markierbar: Leute kopieren Adressen, Bestellnummern und Fehlermeldungen. Setz overscroll-behavior: contain (overscroll-contain) auf innere Scroller, damit das Ende einer Liste in einem Sheet nicht die Seite dahinter scrollt; Safari unterstützt das ab Version 16. Callouts bei langem Druck auf Bilder in deinen Bedienelementen verschwinden mit [-webkit-touch-callout:none], das Safari unterstützt und andere Browser ignorieren.
5. Liegt die Navigation am Daumen?
Native Apps legen ihre 3 bis 5 Hauptbereiche in eine Leiste unten, wo der Daumen schon ist. Im Web braucht diese Leiste den Inset für den Home-Indikator, ein Ausblenden beim Scrollen und einen Weg, dem Rest der Seite ihre Höhe mitzuteilen. Die Bottom Tab Bar erledigt diese drei Dinge, und unser Guide zur React Bottom Navigation Bar baut eine Schritt für Schritt. Behalte ab md eine Sidebar: Eine Tab-Leiste auf einem 1440px-Bildschirm sieht aus wie eine Handy-App, die man in die Breite gezogen hat.
6. Verhält sich der Titel wie auf iOS?
Der Large Title von iOS ist das bekannteste Detail einer iPhone-App: eine 34px-Überschrift, die mit der Seite nach oben scrollt und an einen kleinen Titel in einer Milchglas-Leiste übergibt. Der Large Title Header folgt deinem Finger durch diese Übergabe, rastet ganz ausgeklappt oder ganz eingeklappt ein, wenn du auf halbem Weg aufhörst, und streckt den Titel um bis zu 8 %, wenn du in Safari über den oberen Rand hinaus ziehst. Der Titel bleibt das einzige h1 der Seite, und bei reduzierter Bewegung wird die Übergabe zu einem sofortigen Wechsel. Der Guide zum React Collapsing Header baut diese Übergabe und einen Foto-Hero mit Motion Values.
$ npx wingo-ui@latest add large-title-headerNutz ihn auf Listen der obersten Ebene und auf Einstellungsseiten. Detailseiten lesen sich besser mit mode="compact", das nur die Leiste zeigt, so wie iOS es auf einer geöffneten Unterseite tut.
7. Kannst du zwischen Tabs wischen?
Auf einem Handy sind Tabs Seiten nebeneinander, und ein seitliches Wischen wechselt zwischen ihnen. Einfache Tabs, die nur auf Taps reagieren, wirken wie eine Website. Die Swipeable Tabs legen die Seiten in eine native Scroll-Snap-Reihe. Das Wischen nutzt also das native Momentum des Browsers, und die Unterstreichung dehnt sich zwischen den Tabs, während die Seiten sich bewegen. Ein vertikaler Scroll innerhalb einer Seite wechselt nie den Tab, und ein Tap auf den aktiven Tab scrollt seine Seite zurück nach oben. Der Guide zu React Swipeable Tabs erklärt den Scroll-Snap-Track und den Indikator im Detail.
$ npx wingo-ui@latest add swipeable-tabsWischen ist nie der einzige Weg: Jeder Tab bleibt ein Button, und die Pfeiltasten wechseln zwischen ihnen. Seiten werden standardmäßig erst bei Bedarf gemountet und bleiben danach gemountet (lazy und keepMounted). Eine Seite, zu der du zurückwischst, wird also nicht von Grund auf neu gemountet.
8. Öffnen sich Menüs als Action Sheets?
Auf einem Handy sollten sie das. Tipp in einer nativen App auf den „Mehr“-Button, und ein Sheet mit Aktionen steigt von unten auf, mit einer roten destruktiven Zeile und einem separaten Abbrechen. Ein Dropdown, das an einem 44px-Button hängt, verdeckt den Daumen, der es geöffnet hat. Das Action Sheet öffnet sich auf Handys immer als Sheet. Ab 768px wird es standardmäßig zu einem kompakten Dialog, oder mit desktop="popover" zu einem Menü am Trigger. Der Guide zum React Action Sheet behandelt asynchrone Zeilen und was passiert, wenn eine Aktion scheitert. Du kannst es auch mit await aufrufen, was die Aufrufstelle kurz hält:
show() braucht einen ActionSheetProvider nahe der Wurzel (ohne ihn wirft es einen Fehler) und wird mit der gewählten id aufgelöst, beim Abbrechen mit null. Gibt eine Aktion ein Promise zurück, zeigt ihre Zeile einen Spinner, bis es abgeschlossen ist. Wird das Promise abgelehnt, schüttelt sich die Zeile, und das Sheet bleibt für einen neuen Versuch offen.
$ npx wingo-ui@latest add action-sheetSetz variant="material" für ein einzelnes, durchgehendes Sheet mit Griff und Icon-Zeilen, näher an Android.
9. Öffnen sich Picker und Formulare von unten?
Alles, was auf einem Desktop über der Seite schweben würde, also Filter, ein Date Picker, ein Teilen-Menü oder ein kurzes Formular, sollte auf einem Handy von unten aufsteigen. Dort erreicht es der Daumen und kann es wegziehen. Der kostenlose Drawer ist das Sheet, das der Rest der Bibliothek nutzt: Ziehen zum Schließen, Snap Points, ein scrollbarer Inhalt, der das Sheet erst bewegt, wenn die Liste wieder ganz oben ist, und ab 768px ein zentriertes Sheet, ein Seitenpanel oder ein Dialog, gesetzt mit desktop. Der Guide zum React Bottom Sheet behandelt Snap Points und die Tastatur im Detail.
10. Funktioniert Pull to Refresh?
In einer installierten PWA nur, wenn du die Geste selbst einbaust. Nutzer ziehen eine Liste ganz automatisch nach unten, um sie neu zu laden, und in einer Standalone-PWA fehlt das browsereigene Pull to Refresh oder lädt die ganze Seite neu. Pull To Refresh ergänzt die Geste mit Gummiband-Widerstand und einem Pfeil, iOS-Speichen oder einem Material-Ring:
Die Komponente setzt overscroll-behavior-y: contain auf den Scroller, damit Chrome die Seite nicht neu lädt. Sie ignoriert seitliches Wischen, startet nie einen Zug in einem Eingabefeld oder in einer verschachtelten Liste, die nicht oben steht, und dreht sich mindestens 500ms, damit der Indikator nie nur kurz aufblitzt. Für die Tastatur gibt es keine Geste, also behalte für Maus- und Tastaturnutzer einen Reload-Button oder einen shortcut. Der Guide zu React Pull to Refresh erklärt, warum Safari dafür einen nicht-passiven touchmove-Listener braucht.
11. Kommen Live-Ereignisse wie Benachrichtigungen an?
Ein Toast unten passt zu „Gespeichert“. Eine neue Nachricht, eine Bestellung oder eine Erinnerung, die eintrifft, während jemand die App nutzt, liest sich besser als Banner von oben, wie die Banner des Systems. Der Notification Banner zeigt sie einzeln nacheinander, lässt dich ein Banner nach oben wischen, um es zu schließen, oder nach unten ziehen, um seine Aktionen auszuklappen, und pausiert seinen Timer, solange du es festhältst:
Mounte <NotificationBanner appName="Northwind CRM" /> einmal nahe der Wurzel. Schickst du dieselbe id noch einmal, wird das Banner an Ort und Stelle aktualisiert, statt ein Duplikat darüberzustapeln. Das zählt, wenn ein Socket dasselbe Ereignis zweimal liefert. Das ist ein In-App-Banner; es ersetzt kein Web Push für Leute, die die App geschlossen haben.
12. Was ist mit Haptik, Tastatur und Bewegung?
Drei letzte Details, jedes mit einer Grenze, die du kennen solltest.
Haptik. Der Haptic Button drückt sich mit einer Spring, breitet einen Ripple vom Berührungspunkt aus und spielt beim Berühren einen leichten Tick. Sein mode="hold" füllt sich, während du zum Bestätigen eines Löschvorgangs gedrückt hältst. Der Tick nutzt navigator.vibrate, das in Chromium auf Android nach dem ersten Tap funktioniert. Safari auf dem iPhone hat keine Vibration API, und seit iOS 26.5 kann eine Webseite den System-Tick eines versteckten Switches nicht mehr auslösen. Auf einem aktuellen iPhone tragen also der Druck und die Füllung das Feedback allein. Unser Guide zur Web Vibration API zeigt, was noch tickt.
Die Tastatur. Auf iOS, und auf Android seit Chrome 108, verdeckt die Tastatur fixierte Leisten, statt sie nach oben zu schieben. Ein Chat-Composer, der unten fixiert ist, muss ihr folgen. Der Hook useKeyboardInset meldet die verdeckte Höhe als Motion Value, und der Guide zur iOS-Safari-Tastatur baut den Composer Schritt für Schritt.
Bewegung. Native Übergänge nutzen Springs und respektieren die Systemeinstellung für reduzierte Bewegung. Die Komponenten in diesem Beitrag holen ihre Springs aus einem gemeinsamen Satz in lib/motion und lesen die Einstellung für reduzierte Bewegung. Die Übergabe des Titels wird dann zu einem sofortigen Wechsel, das Action Sheet blendet ein, statt zu gleiten, und der Haptic Button lässt Eindrücken, Ripple und Schütteln weg.
Womit solltest du anfangen?
Fang mit denen an, die nichts kosten, denn sie beheben die Anzeichen, die jeder bemerkt: Start (1), Taps (2), Zoom (3) und das Dokument-Verhalten (4) sind CSS und ein Manifest. Dann die Sheets: npx wingo-ui@latest add drawer installiert den kostenlosen Drawer. Bottom Tab Bar, Large Title, Swipeable Tabs, Action Sheet, Pull To Refresh, Banner, Haptic Button und der Tastatur-Hook gehören zu Wingo UI Pro, mit Live-Demos auf jeder Komponentenseite. So kannst du das Native App Feeling auf deinem Handy ausprobieren, bevor du etwas installierst.
Häufige Fragen
Was braucht eine PWA, damit sie sich wie eine native App anfühlt?
Starte sie mit display standalone und viewport-fit=cover, gib jeder fixierten Leiste die Safe Area Insets als Padding und behebe das Verhalten, das eine Webseite verrät: langsame oder blitzende Taps, Dropdown-Menüs, wo ein Handy ein Sheet erwartet, fixierte Leisten, die von der Tastatur verdeckt werden, und Seiten, die wie Dokumente scrollen. Visuelle Details wie Large Titles kommen danach.
Kann eine Web App auf dem iPhone haptisches Feedback nutzen?
Kaum. Safari hat keine Vibration API, und seit iOS 26.5 kommt der System-Tick eines nativen Switches nur, wenn die Person ihn selbst per Tap umschaltet. Eine Web App kann also nicht auf Abruf ticken. Android Chrome unterstützt navigator.vibrate nach dem ersten Tap, also behandle Haptik als Extra und gib jedem Tick eine sichtbare Entsprechung.
Sollte eine Web App auf Android das iOS-Design kopieren?
Kopier das Verhalten, das beide Plattformen teilen, etwa Sheets von unten, Tabs zum Wischen und sofortiges Feedback beim Drücken. Plattformspezifische Optik ist eine Entscheidung: Mehrere Komponenten von Wingo UI bieten eine iOS- und eine Material-Variante, zum Beispiel das Action Sheet und der Indikator von Pull To Refresh.
Woran erkennt man, dass eine App nur eine Website ist?
Die üblichen Anzeichen sind ein grauer Kasten über getippten Links, eine Seite, die zoomt, wenn du doppelt auf einen Button tippst oder ein kleines Eingabefeld fokussierst, Menüs, die als winzige Popover aufgehen, die Tastatur über einer unteren Leiste, Text, der markiert wird, wenn du einen Button hältst, und die ganze Seite, die hinter einer scrollenden Liste federt.
Sind diese Komponenten kostenlos?
Button und Drawer, die die Grundlagen für Taps und Sheets liefern, sind in Wingo UI kostenlos. Bottom Tab Bar, Large Title Header, Swipeable Tabs, Action Sheet, Pull To Refresh, Notification Banner, Haptic Button und der Hook useKeyboardInset gehören zu Wingo UI Pro.
- Mobile UI
- PWA
- iOS
- React
- Native Feeling


