diff --git a/config/docs.php b/config/docs.php
index be584a09..03cda4f2 100644
--- a/config/docs.php
+++ b/config/docs.php
@@ -52,6 +52,34 @@
'mobile' => [
4 => [
'the-basics/native-components' => 'the-basics/native-ui',
+ 'the-basics/dialog' => 'the-basics/dialogs',
+ 'getting-started/deployment' => 'publishing/introduction',
+ 'plugins/vibe' => 'digging-deeper/websockets',
+
+ // Concepts section renamed to Digging Deeper
+ 'concepts/authentication' => 'digging-deeper/authentication',
+ 'concepts/databases' => 'digging-deeper/databases',
+ 'concepts/deep-links' => 'digging-deeper/deep-links',
+ 'concepts/push-notifications' => 'digging-deeper/push-notifications',
+ 'concepts/queues' => 'digging-deeper/queues',
+ 'concepts/security' => 'digging-deeper/security',
+
+ // SuperNative overview page now lives in the Architecture section
+ 'super-native/introduction' => 'architecture/super-native',
+
+ // Some SuperNative pages moved into The Basics
+ 'super-native/navigation' => 'the-basics/routing',
+ 'super-native/layouts' => 'the-basics/layouts',
+ 'super-native/events' => 'the-basics/events',
+
+ // Remaining SuperNative pages flattened into the Digging Deeper section
+ 'super-native/lifecycle-hooks' => 'digging-deeper/lifecycle-hooks',
+ 'super-native/data-binding' => 'digging-deeper/data-binding',
+ 'super-native/reactivity' => 'digging-deeper/reactivity',
+ 'super-native/theming' => 'digging-deeper/theming',
+ 'super-native/gestures' => 'digging-deeper/gestures',
+ 'super-native/search' => 'digging-deeper/search',
+ 'super-native/accessibility' => 'digging-deeper/accessibility',
],
],
],
diff --git a/resources/views/docs/mobile/2/getting-started/deployment.md b/resources/views/docs/mobile/2/getting-started/deployment.md
index 68bed419..bd2d687d 100644
--- a/resources/views/docs/mobile/2/getting-started/deployment.md
+++ b/resources/views/docs/mobile/2/getting-started/deployment.md
@@ -52,7 +52,7 @@ the stores. You should consult the [App Store Connect Help](https://developer.ap
[Play Console Help](https://support.google.com/googleplay/android-developer/?hl=en-GB#topic=3450769) documentation for
detailed and up-to-date guidance on how to prepare your app submissions and listings.
-If you want more hands-on support, we happily work with our [Partners](/partners) to support them releasing their apps.
+If you want more [hands-on support](/consulting), we happily work with our [Partners](/partners) to support them releasing their apps.
diff --git a/resources/views/docs/mobile/3/getting-started/deployment.md b/resources/views/docs/mobile/3/getting-started/deployment.md
index b5742691..66fdb5fe 100644
--- a/resources/views/docs/mobile/3/getting-started/deployment.md
+++ b/resources/views/docs/mobile/3/getting-started/deployment.md
@@ -52,7 +52,7 @@ the stores. You should consult the [App Store Connect Help](https://developer.ap
[Play Console Help](https://support.google.com/googleplay/android-developer/?hl=en-GB#topic=3450769) documentation for
detailed and up-to-date guidance on how to prepare your app submissions and listings.
-If you want more hands-on support, we happily work with our [Partners](/partners) to support them releasing their apps.
+If you want more [hands-on support](/consulting), we happily work with our [Partners](/partners) to support them releasing their apps.
diff --git a/resources/views/docs/mobile/4/architecture/_index.md b/resources/views/docs/mobile/4/architecture/_index.md
index 23fd3cd6..5bccd3fb 100644
--- a/resources/views/docs/mobile/4/architecture/_index.md
+++ b/resources/views/docs/mobile/4/architecture/_index.md
@@ -1,4 +1,4 @@
---
title: Architecture
-order: 80
+order: 10
---
diff --git a/resources/views/docs/mobile/4/architecture/about-the-new-architecture.md b/resources/views/docs/mobile/4/architecture/about-the-new-architecture.md
index 57a13307..79708bac 100644
--- a/resources/views/docs/mobile/4/architecture/about-the-new-architecture.md
+++ b/resources/views/docs/mobile/4/architecture/about-the-new-architecture.md
@@ -3,12 +3,12 @@ title: About the New Architecture
order: 10
---
-Since v1, NativePHP for Mobile has run your Laravel app on the device itself — no server, no network round-trip. In
-v3 and earlier, your app's UI rendered as HTML inside a web view. That model is productive and familiar, and it's
-[still fully supported](../super-native/introduction#is-the-web-view-still-an-option). But it puts a browser between
+NativePHP for Mobile runs your Laravel app on the device itself — no server, no network round-trip. Traditionally,
+your app's UI rendered as HTML inside a web view. That model is productive and familiar, and it's
+[still fully supported](../architecture/super-native#is-the-web-view-still-an-option). But it puts a browser between
your app and the platform, and some things can only feel truly native when they *are* native.
-The new architecture — [SuperNative](../super-native/introduction) — removes that layer entirely. Your screens are
+The new architecture — [SuperNative](../architecture/super-native) — removes that layer entirely. Your screens are
real SwiftUI and Jetpack Compose views, created and updated directly by your PHP code. Here's why we built it, and
what it changes.
@@ -68,8 +68,8 @@ app faster. If your app is happy in the web view, it will keep working exactly a
## Should you use it today?
-SuperNative is **the default** in v4: new apps render native screens from the very first route. It's in beta, so
-expect rapid iteration — and if you'd rather wait, [opting out](../super-native/introduction#is-the-web-view-still-an-option)
+SuperNative is **the default**: new apps render native screens from the very first route. It's in beta, so
+expect rapid iteration — and if you'd rather wait, [opting out](../architecture/super-native#is-the-web-view-still-an-option)
is one route and one component.
Ready to go deeper? Start with [The Renderer](renderer).
diff --git a/resources/views/docs/mobile/4/architecture/glossary.md b/resources/views/docs/mobile/4/architecture/glossary.md
index 51ee86f6..5601ccdd 100644
--- a/resources/views/docs/mobile/4/architecture/glossary.md
+++ b/resources/views/docs/mobile/4/architecture/glossary.md
@@ -7,7 +7,7 @@ Definitions for the terms used throughout the Architecture section.
## SuperNative
-The engine behind native rendering in NativePHP for Mobile v4. It spans everything described in this section:
+The engine behind native rendering in NativePHP for Mobile. It spans everything described in this section:
components, the render pipeline, the shared-memory boundary and the platform renderers.
[About the New Architecture](about-the-new-architecture) is the best starting point.
diff --git a/resources/views/docs/mobile/4/architecture/overview.md b/resources/views/docs/mobile/4/architecture/overview.md
deleted file mode 100644
index a9d2fc8b..00000000
--- a/resources/views/docs/mobile/4/architecture/overview.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-title: Architecture Overview
-order: 1
----
-
-Welcome! This section is a look under the hood of [SuperNative](../super-native/introduction), the engine that powers
-native rendering in NativePHP for Mobile v4. It explains how your Blade templates become real SwiftUI and Jetpack
-Compose views, how state flows between PHP and the screen, and why the whole thing is fast.
-
-
-
-You **don't** need to read any of this to build apps with NativePHP. If you're here to ship an app, start with the
-[Quick Start](../getting-started/quick-start) and [The Basics](../the-basics/overview) instead. This section is for
-the curious — and for plugin authors and contributors who want to understand the machinery they're building on.
-
-
-
-These pages describe the internals as they exist today. SuperNative is in beta and moving quickly, so details may
-evolve — the concepts, however, are stable.
-
-## Table of Contents
-
-- [About the New Architecture](about-the-new-architecture) — why we rebuilt rendering from the ground up, and what
- it means for your apps.
-
-### Rendering
-
-- [The Renderer](renderer) — the rendering system at the heart of SuperNative, and the goals that shaped it.
-- [Render, Publish, and Mount](render-publish-mount) — the three-phase pipeline that turns your PHP into pixels.
-- [Cross-Platform Implementation](cross-platform-implementation) — how one shared core keeps iOS and Android in
- perfect agreement.
-- [Subtree Reuse](subtree-reuse) — the optimizations that make re-rendering cheap, automatically.
-- [Threading Model](threading-model) — which threads do what, and how your UI stays responsive.
-
-### Build Tools
-
-- [Embedded PHP](embedded-php) — how a full PHP runtime ends up inside your app, built in lockstep with the
- framework.
-
-### Reference
-
-- [Glossary](glossary) — every term used in these pages, defined in one place.
-
-If anything here is unclear or you'd like more depth on a particular topic, we'd love to hear from you.
diff --git a/resources/views/docs/mobile/4/architecture/super-native.md b/resources/views/docs/mobile/4/architecture/super-native.md
new file mode 100644
index 00000000..94b58f87
--- /dev/null
+++ b/resources/views/docs/mobile/4/architecture/super-native.md
@@ -0,0 +1,130 @@
+---
+title: SuperNative Introduction
+order: 5
+---
+
+## What is SuperNative?
+
+SuperNative is our name for a combination of technologies that enable PHP to produce platform-native UI at
+blistering speeds.
+
+While SuperNative uses HTML-like syntax, **there is no need for a web view**. Your apps built with Blade
+can fully leverage the native components and performance of each platform's UI tools, directly from PHP — SwiftUI
+on iOS and Jetpack Compose on Android.
+
+Your app's screens are not web views rendering HTML. They are real, platform-native views, built and
+updated by your PHP code. Same Laravel app, same Blade templates, two genuine native UIs.
+
+You don't need to think about the right syntax for Android vs iOS, just use EDGE.
+
+SuperNative is **the default**. New apps render native screens from the very first route — no configuration
+required.
+
+
+
+Prefer to keep building with the web view? [Opting out](#is-the-web-view-still-an-option) of native UIs couldn't
+be simpler.
+
+
+
+## What SuperNative is not
+
+SuperNative is not a fully custom renderer, like Skia or Impeller. It does not attempt to create pixel-perfect
+cross-platform user interfaces. Instead it *embraces* the differences and simply smooths over them with a single
+consistent syntax.
+
+It's not another virtual machine on top of or adjacent to PHP trying to convert *all* instructions to/from native
+equivalents. It's explicitly focused on turning PHP objects that conform to a known interface into a fixed-length
+byte array that can be processed by an explicit native-side interpreter.
+
+It is also not a transpiler or HTML-to-native converter. We've built our own Blade engine that converts Blade components
+into a simplified binary format instead of HTML ([The Renderer](renderer)) and passes the
+
+## Try it now
+
+The fastest way to see SuperNative is to run the demo app,
+[`nativephp/super-native`](https://github.com/nativephp/super-native), on a simulator or device.
+
+You'll need a working NativePHP for Mobile [development environment](../getting-started/environment-setup) first
+(Xcode for iOS, Android Studio for Android). Then clone the demo, install it, and run it:
+
+```shell
+git clone https://github.com/nativephp/super-native
+cd super-native
+composer install
+php artisan native:install
+php artisan native:run
+```
+
+`native:run` builds the app and launches it on your connected device or simulator. Explore the source to see how
+the screens are built, then start swapping in your own.
+
+## How it works
+
+SuperNative builds on three ideas working together:
+
+- **Shared memory with PHP** — the native layer and your PHP application share memory directly, so there's no
+ network round-trip, no serialization overhead, and no waiting on a web view bridge. State changes flow between
+ PHP and the native UI almost instantly.
+- **Livewire-like components** — each screen is driven by a PHP component class that holds its state and behavior,
+ just like a Livewire component. User interactions call your methods, your properties update, and the UI
+ re-renders to match.
+- **Blade components for DX** — you define your UI with the same [EDGE component](../edge-components/introduction)
+ syntax you already know. Familiar, expressive Blade templates compile down to native SwiftUI and Compose views.
+
+If you've built anything with Livewire, you already know how to build with SuperNative.
+
+For a look under the hood — how Blade becomes SwiftUI and Compose, how state flows across the shared-memory
+boundary, and the threading model behind it — see the rest of the [Architecture](about-the-new-architecture) section.
+
+
+
+You **don't** need to read any of this to build apps with NativePHP. If you're here to ship an app, start with the
+[Quick Start](../getting-started/quick-start) and [The Basics](../the-basics/overview) instead. This section is for
+the curious — and for plugin authors and contributors who want to understand the machinery they're building on.
+
+
+
+## Why SuperNative?
+
+Two reasons above all:
+
+- **Performance** — native views render and animate at full platform speed. No web view startup cost, no DOM, no
+ JavaScript bridge. Scrolling, transitions and gestures feel exactly the way users expect because they're powered
+ by the same UI frameworks every other native app uses.
+- **Accessibility** — SwiftUI and Jetpack Compose come with the platform's accessibility support built in.
+ Screen readers, dynamic type, contrast settings and assistive controls work with your app out of the box,
+ rather than being approximated through a browser.
+
+There's more detail [in this blog article](/blog/supernative).
+
+## Is the web view still an option?
+
+Yes, but instead of being the default, it's now a component that you add to a native view. To make your app behave
+the same way that NativePHP for Mobile versions before v4 did, you can do something like this:
+
+```php
+// routes/mobile.php
+Route::native('/home', WebViewScreen::class);
+
+// webviewscreen.blade.php
+
+
+// routes/web.php
+Route::view('/', 'welcome');
+```
+
+Then just set `NATIVEPHP_START_URL=/home` in your `.env`.
+
+This way your existing web view-based app can keep on working and you can start adopt SuperNative one screen at a time
+whenever you're ready — or not at all.
+
+## For Plugin Developers
+
+SuperNative contains **no breaking changes** to our plugin architecture.
+
+It actually expands what your plugins are capable of by giving you a standardized target for UI elements. That means your
+plugins can ship fully native EDGE components that you know will work consistently for developers using your plugin.
+
+No need to make your plugins UI-less abstractions or support multiple flavors of front-end tooling; simply create and ship
+EDGE components and every consumer of your plugin will see the UI you intended.
diff --git a/resources/views/docs/mobile/4/concepts/_index.md b/resources/views/docs/mobile/4/concepts/_index.md
deleted file mode 100644
index 6e3f1cfd..00000000
--- a/resources/views/docs/mobile/4/concepts/_index.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Concepts
-order: 40
----
diff --git a/resources/views/docs/mobile/4/digging-deeper/_index.md b/resources/views/docs/mobile/4/digging-deeper/_index.md
new file mode 100644
index 00000000..a4ac31b2
--- /dev/null
+++ b/resources/views/docs/mobile/4/digging-deeper/_index.md
@@ -0,0 +1,4 @@
+---
+title: Digging Deeper
+order: 40
+---
diff --git a/resources/views/docs/mobile/4/super-native/accessibility.md b/resources/views/docs/mobile/4/digging-deeper/accessibility.md
similarity index 100%
rename from resources/views/docs/mobile/4/super-native/accessibility.md
rename to resources/views/docs/mobile/4/digging-deeper/accessibility.md
diff --git a/resources/views/docs/mobile/4/concepts/authentication.md b/resources/views/docs/mobile/4/digging-deeper/authentication.md
similarity index 100%
rename from resources/views/docs/mobile/4/concepts/authentication.md
rename to resources/views/docs/mobile/4/digging-deeper/authentication.md
diff --git a/resources/views/docs/mobile/4/super-native/data-binding.md b/resources/views/docs/mobile/4/digging-deeper/data-binding.md
similarity index 100%
rename from resources/views/docs/mobile/4/super-native/data-binding.md
rename to resources/views/docs/mobile/4/digging-deeper/data-binding.md
diff --git a/resources/views/docs/mobile/4/concepts/databases.md b/resources/views/docs/mobile/4/digging-deeper/databases.md
similarity index 97%
rename from resources/views/docs/mobile/4/concepts/databases.md
rename to resources/views/docs/mobile/4/digging-deeper/databases.md
index c25122ef..70db2399 100644
--- a/resources/views/docs/mobile/4/concepts/databases.md
+++ b/resources/views/docs/mobile/4/digging-deeper/databases.md
@@ -24,7 +24,7 @@ When writing migrations, you need to consider any special recommendations for wo
For example, prior to Laravel 11, SQLite foreign key constraints are turned off by default. If your application relies
upon foreign key constraints, [you need to enable SQLite support for them](https://laravel.com/docs/database#configuration) before running your migrations.
-**It's important to test your migrations on [prod builds](/docs/mobile/1/getting-started/development#releasing)
+**It's important to test your migrations on [prod builds](../publishing/introduction#releasing)
before releasing updates!** You don't want to accidentally delete your user's data when they update your app.
## Seeding data with migrations
@@ -117,7 +117,7 @@ If anything, you should provide a client key that **only** allows client apps to
authenticated your user, you can pass an access token back to your mobile app and use this for communicating with your
API.
-Store these tokens on your users' devices securely with the [`SecureStorage`](/docs/mobile/1/apis/secure-storage) API.
+Store these tokens on your users' devices securely with the [`SecureStorage`](../plugins/core/secure-storage) API.
It's a good practice to ensure these tokens have high entropy so that they are very hard to guess and a short lifespan.
Generating tokens is cheap; leaking personal customer data can get _very_ expensive!
diff --git a/resources/views/docs/mobile/4/concepts/deep-links.md b/resources/views/docs/mobile/4/digging-deeper/deep-links.md
similarity index 100%
rename from resources/views/docs/mobile/4/concepts/deep-links.md
rename to resources/views/docs/mobile/4/digging-deeper/deep-links.md
diff --git a/resources/views/docs/mobile/4/super-native/gestures.md b/resources/views/docs/mobile/4/digging-deeper/gestures.md
similarity index 98%
rename from resources/views/docs/mobile/4/super-native/gestures.md
rename to resources/views/docs/mobile/4/digging-deeper/gestures.md
index b5f81316..266ad503 100644
--- a/resources/views/docs/mobile/4/super-native/gestures.md
+++ b/resources/views/docs/mobile/4/digging-deeper/gestures.md
@@ -136,6 +136,6 @@ the native side plays it locally on touch.
Three tools, three jobs: **`animate-*` props** for state-driven motion (a value changed — ease to it), **press
feedback** for instant touch response, and **shared values** for finger-tracking or refresh-rate animation. For
-motion *between screens*, see [Navigation](navigation#custom-transitions).
+motion *between screens*, see [Navigation](../the-basics/routing#custom-transitions).
diff --git a/resources/views/docs/mobile/4/super-native/lifecycle-hooks.md b/resources/views/docs/mobile/4/digging-deeper/lifecycle-hooks.md
similarity index 97%
rename from resources/views/docs/mobile/4/super-native/lifecycle-hooks.md
rename to resources/views/docs/mobile/4/digging-deeper/lifecycle-hooks.md
index 3cab6519..b5c2807b 100644
--- a/resources/views/docs/mobile/4/super-native/lifecycle-hooks.md
+++ b/resources/views/docs/mobile/4/digging-deeper/lifecycle-hooks.md
@@ -16,7 +16,7 @@ on top, **resumes** when those screens pop away, and is **unmounted** when it fi
## mount()
Runs **once**, right after the screen is pushed onto the stack. This is where you load the screen's initial data
-and read any [route params or navigation data](../super-native/navigation#reading-params-and-data).
+and read any [route params or navigation data](../the-basics/routing#reading-params-and-data).
```php
class ProductScreen extends NativeComponent
diff --git a/resources/views/docs/mobile/4/concepts/push-notifications.md b/resources/views/docs/mobile/4/digging-deeper/push-notifications.md
similarity index 100%
rename from resources/views/docs/mobile/4/concepts/push-notifications.md
rename to resources/views/docs/mobile/4/digging-deeper/push-notifications.md
diff --git a/resources/views/docs/mobile/4/concepts/queues.md b/resources/views/docs/mobile/4/digging-deeper/queues.md
similarity index 94%
rename from resources/views/docs/mobile/4/concepts/queues.md
rename to resources/views/docs/mobile/4/digging-deeper/queues.md
index 67551806..f890c06d 100644
--- a/resources/views/docs/mobile/4/concepts/queues.md
+++ b/resources/views/docs/mobile/4/digging-deeper/queues.md
@@ -83,7 +83,7 @@ NativePHP manages the worker lifecycle natively on both platforms.
## Things to Note
-- The queue worker requires [ZTS (Thread-Safe) PHP](/docs/mobile/3/getting-started/changelog), which is included by default in v3.1+.
+- The queue worker requires ZTS (Thread-Safe) PHP, which NativePHP includes by default.
- Only the `database` queue connection is supported. This uses the same SQLite database as your app.
- Jobs are persisted to the database, so they survive app restarts.
- If a job fails, Laravel's standard retry and failure handling applies.
\ No newline at end of file
diff --git a/resources/views/docs/mobile/4/super-native/reactivity.md b/resources/views/docs/mobile/4/digging-deeper/reactivity.md
similarity index 98%
rename from resources/views/docs/mobile/4/super-native/reactivity.md
rename to resources/views/docs/mobile/4/digging-deeper/reactivity.md
index 7f1c9f43..a1e1a690 100644
--- a/resources/views/docs/mobile/4/super-native/reactivity.md
+++ b/resources/views/docs/mobile/4/digging-deeper/reactivity.md
@@ -93,6 +93,6 @@ Accepted forms: `native:poll` (default 2s), `native:poll="500ms"` / `native:poll
Polling drives a full re-render, so keep intervals as long as the UX allows. For push-style updates that arrive
-when something actually happens rather than on a timer, listen for an [event](events) instead.
+when something actually happens rather than on a timer, listen for an [event](../the-basics/events) instead.
diff --git a/resources/views/docs/mobile/4/super-native/search.md b/resources/views/docs/mobile/4/digging-deeper/search.md
similarity index 89%
rename from resources/views/docs/mobile/4/super-native/search.md
rename to resources/views/docs/mobile/4/digging-deeper/search.md
index 86cb6431..0235e1d7 100644
--- a/resources/views/docs/mobile/4/super-native/search.md
+++ b/resources/views/docs/mobile/4/digging-deeper/search.md
@@ -9,7 +9,7 @@ A screen can present a native search bar in its navigation chrome and feed it fr
a **static** corpus the platform filters for you, and a **dynamic** handler you implement for server- or
database-backed results.
-The search bar itself is shown by the [layout](layouts) chrome — via `NavBar::searchBar()` on a stack, or
+The search bar itself is shown by the [layout](../the-basics/layouts) chrome — via `NavBar::searchBar()` on a stack, or
`Tab::search()` on a tab. This page covers the component side: producing the results.
## Static search
@@ -59,7 +59,7 @@ Or as a dedicated search tab:
Tab::search('Search', icon: 'magnifyingglass', placeholder: 'Find anything');
```
-See [Layouts](layouts) for where these builders live and how a layout wraps a screen.
+See [Layouts](../the-basics/layouts) for where these builders live and how a layout wraps a screen.
diff --git a/resources/views/docs/mobile/4/concepts/security.md b/resources/views/docs/mobile/4/digging-deeper/security.md
similarity index 100%
rename from resources/views/docs/mobile/4/concepts/security.md
rename to resources/views/docs/mobile/4/digging-deeper/security.md
diff --git a/resources/views/docs/mobile/4/super-native/theming.md b/resources/views/docs/mobile/4/digging-deeper/theming.md
similarity index 100%
rename from resources/views/docs/mobile/4/super-native/theming.md
rename to resources/views/docs/mobile/4/digging-deeper/theming.md
diff --git a/resources/views/docs/mobile/4/plugins/vibe.md b/resources/views/docs/mobile/4/digging-deeper/websockets.md
similarity index 96%
rename from resources/views/docs/mobile/4/plugins/vibe.md
rename to resources/views/docs/mobile/4/digging-deeper/websockets.md
index c7400e03..63e1e8e6 100644
--- a/resources/views/docs/mobile/4/plugins/vibe.md
+++ b/resources/views/docs/mobile/4/digging-deeper/websockets.md
@@ -1,8 +1,15 @@
---
-title: Vibe (Websockets)
-order: 250
+title: WebSockets
+order: 350
---
+
+
+WebSockets functionality is provided by the (free) [Vibe plugin](../plugins/core/vibe). To use this feature, you must
+install the `nativephp/mobile-vibe` Composer package.
+
+
+
## Overview
Vibe brings live server events into your NativePHP Mobile app over the **Pusher protocol** — so it works with
@@ -149,7 +156,7 @@ Vibe::withToken($freshToken);
-Store tokens with the [SecureStorage](./core/secure-storage) plugin so they live in the device keychain/keystore rather
+Store tokens with the [SecureStorage](../plugins/core/secure-storage) plugin so they live in the device keychain/keystore rather
than in plain application state.
@@ -364,7 +371,7 @@ Presence only. Runs when a member leaves.
- **Foreground-only:** Websockets are foreground-only on mobile. The OS suspends the socket when your app goes to the
background. For delivery while the app is closed, use push notifications via the
- [Firebase plugin](./core/firebase).
+ [Firebase plugin](../plugins/core/firebase).
- **Liveness, not source of truth:** Treat websocket events as a *liveness* signal, not authoritative state. On
reconnect, refetch the canonical data from your backend.
- **Auto-teardown:** Subscriptions are removed automatically when the component unmounts. Leaving a screen leaves its
diff --git a/resources/views/docs/mobile/4/edge-components/bottom-nav.md b/resources/views/docs/mobile/4/edge-components/bottom-nav.md
index 88068e89..e31afa24 100644
--- a/resources/views/docs/mobile/4/edge-components/bottom-nav.md
+++ b/resources/views/docs/mobile/4/edge-components/bottom-nav.md
@@ -4,7 +4,7 @@ order: 120
---
> [!IMPORTANT]
-> **Prefer the [Layout model](../super-native/layouts).** Declare your app's bottom tabs with the `TabBar`
+> **Prefer the [Layout model](../the-basics/layouts).** Declare your app's bottom tabs with the `TabBar`
> builder in a `NativeLayout` class rather than placing `` in a screen. This page documents the
> inline element, which still works but is no longer the recommended approach.
@@ -60,7 +60,7 @@ above the indicator, mirroring iOS `UITabBar`.
## Children
> [!IMPORTANT]
-> In the [Layout model](../super-native/layouts#builder-reference) a bottom-nav item is a `Tab` — use that builder
+> In the [Layout model](../the-basics/layouts#builder-reference) a bottom-nav item is a `Tab` — use that builder
> rather than placing `` inline.
A `` can contain up to 5 `` elements.
@@ -94,7 +94,7 @@ Any `url` that doesn't match a registered native route will exit to the web view
## Builder API
-When a `` is supplied by a [layout](../super-native/layouts), you build it fluently with the `TabBar`
+When a `` is supplied by a [layout](../the-basics/layouts), you build it fluently with the `TabBar`
and `Tab` builders rather than writing it in Blade.
```php
diff --git a/resources/views/docs/mobile/4/edge-components/gesture-area.md b/resources/views/docs/mobile/4/edge-components/gesture-area.md
index 82e3a30c..37edc864 100644
--- a/resources/views/docs/mobile/4/edge-components/gesture-area.md
+++ b/resources/views/docs/mobile/4/edge-components/gesture-area.md
@@ -6,7 +6,7 @@ order: 225
## Overview
Captures a vertical pan/drag gesture over its content and writes the translation to a bound
-[shared value](../super-native/gestures), so the drag can drive animation on the UI thread with no PHP round-trip.
+[shared value](../digging-deeper/gestures), so the drag can drive animation on the UI thread with no PHP round-trip.
Children render normally — gesture detection wraps the whole content frame.
@verbatim
@@ -23,7 +23,7 @@ Children render normally — gesture detection wraps the whole content frame.
## Props
-- `pan-y` - A [`SharedValue`](../super-native/gestures) that receives the vertical drag translation (required for
+- `pan-y` - A [`SharedValue`](../digging-deeper/gestures) that receives the vertical drag translation (required for
the gesture to do anything). Bind it, then read it from animatable props (`translate-y`, `opacity`, `scale`, …)
on the children.
@@ -44,6 +44,6 @@ public function onRelease(float $value): void
Per-frame drag values stay on the native side and never round-trip through PHP — only `@drag-end` calls back. See
-[Gestures & Animation](../super-native/gestures) for shared values and interpolation formulas.
+[Gestures & Animation](../digging-deeper/gestures) for shared values and interpolation formulas.
diff --git a/resources/views/docs/mobile/4/edge-components/icon.md b/resources/views/docs/mobile/4/edge-components/icon.md
index a352c1a4..47e40a70 100644
--- a/resources/views/docs/mobile/4/edge-components/icon.md
+++ b/resources/views/docs/mobile/4/edge-components/icon.md
@@ -25,7 +25,7 @@ All [shared layout and style attributes](layout) are supported, plus:
- `size` - Icon size in dp (optional, float, default: `24`)
- `color` - Icon color as hex string (optional, default: platform default)
- `a11y-label` - Accessibility label (optional). Icons are decorative by default — hidden from screen readers
- unless this is set. Label any icon that conveys meaning on its own. See [Accessibility](../super-native/accessibility)
+ unless this is set. Label any icon that conveys meaning on its own. See [Accessibility](../digging-deeper/accessibility)
diff --git a/resources/views/docs/mobile/4/edge-components/image.md b/resources/views/docs/mobile/4/edge-components/image.md
index 66b5e723..7f1484cc 100644
--- a/resources/views/docs/mobile/4/edge-components/image.md
+++ b/resources/views/docs/mobile/4/edge-components/image.md
@@ -23,7 +23,7 @@ All [shared layout and style attributes](layout) are supported, plus:
- `2` / `3` — fill (scale to fill bounds, cropping excess)
- `tint-color` - Apply a color tint as hex string (optional)
- `alt` - Accessibility alt text (optional). When set, screen readers announce the image with this label; when
- omitted, the image is treated as decorative and hidden from VoiceOver/TalkBack. See [Accessibility](../super-native/accessibility)
+ omitted, the image is treated as decorative and hidden from VoiceOver/TalkBack. See [Accessibility](../digging-deeper/accessibility)
diff --git a/resources/views/docs/mobile/4/edge-components/layout.md b/resources/views/docs/mobile/4/edge-components/layout.md
index 04b716a8..885c9fef 100644
--- a/resources/views/docs/mobile/4/edge-components/layout.md
+++ b/resources/views/docs/mobile/4/edge-components/layout.md
@@ -220,7 +220,7 @@ typically applied to your outermost column.
- `safe-area-top` - Inset only the top edge (status bar / notch)
- `safe-area-bottom` - Inset only the bottom edge (home indicator)
-See [Safe Area](../the-basics/safe-area) for the full picture, including how the framework's [layout](../super-native/layouts)
+See [Safe Area](../the-basics/safe-area) for the full picture, including how the framework's [layout](../the-basics/layouts)
chrome already handles safe-area insets for you.
## Visibility
diff --git a/resources/views/docs/mobile/4/edge-components/list.md b/resources/views/docs/mobile/4/edge-components/list.md
index 28649324..40179306 100644
--- a/resources/views/docs/mobile/4/edge-components/list.md
+++ b/resources/views/docs/mobile/4/edge-components/list.md
@@ -79,7 +79,7 @@ content slots.
- `trailingSwitch` - Boolean value for a trailing switch [Android]
- `trailingIconButton` - Icon name for a tappable trailing button
- `trailing-a11y-label` - Accessibility label for the trailing icon button (recommended whenever
- `trailingIconButton` is set). See [Accessibility](../super-native/accessibility)
+ `trailingIconButton` is set). See [Accessibility](../digging-deeper/accessibility)
- `trailing-menu` - Attach a tap-to-open dropdown to the row's trailing edge. See [Menus](menus)
Independent of the mutually-exclusive slot above, a row can also show a stack of small status icons:
diff --git a/resources/views/docs/mobile/4/edge-components/menus.md b/resources/views/docs/mobile/4/edge-components/menus.md
index 0810bdef..f5f3bf5c 100644
--- a/resources/views/docs/mobile/4/edge-components/menus.md
+++ b/resources/views/docs/mobile/4/edge-components/menus.md
@@ -12,7 +12,7 @@ standalone element — you build a list of actions and attach it with an attribu
- `:trailing-menu` on a [list item](list) — opens from the row's trailing edge.
All three share one item model: an array of `NavAction`, the same builder used for
-[nav-bar menus](../super-native/layouts#builder-reference).
+[nav-bar menus](../the-basics/layouts#builder-reference).
## Building the items
@@ -76,4 +76,4 @@ The menu-relevant `NavAction` methods:
- `destructive(bool = true)` — tint the item as destructive
- `NavAction::divider()` — a separator row
-See the [`NavAction` builder](../super-native/layouts#builder-reference) for the full list.
+See the [`NavAction` builder](../the-basics/layouts#builder-reference) for the full list.
diff --git a/resources/views/docs/mobile/4/edge-components/side-nav.md b/resources/views/docs/mobile/4/edge-components/side-nav.md
index f5c43d5d..cd393798 100644
--- a/resources/views/docs/mobile/4/edge-components/side-nav.md
+++ b/resources/views/docs/mobile/4/edge-components/side-nav.md
@@ -4,7 +4,7 @@ order: 370
---
> [!IMPORTANT]
-> **Prefer the [Layout model](../super-native/layouts#drawer-navigation).** Provide a slide-out drawer with the
+> **Prefer the [Layout model](../the-basics/layouts#drawer-navigation).** Provide a slide-out drawer with the
> `Drawer` builder from a `NativeLayout` rather than placing `` in a screen. This page documents
> the inline element, which still works but is no longer the recommended approach.
@@ -80,7 +80,7 @@ On iOS, gesture support is always enabled for the side nav.
## Children
> [!IMPORTANT]
-> In the [Layout model](../super-native/layouts#drawer-navigation) a drawer's contents are any Blade view — build
+> In the [Layout model](../the-basics/layouts#drawer-navigation) a drawer's contents are any Blade view — build
> the header, items, and groups below with standard EDGE components inside a `Drawer`, rather than the
> `` elements.
diff --git a/resources/views/docs/mobile/4/edge-components/text-input.md b/resources/views/docs/mobile/4/edge-components/text-input.md
index 2d36dd10..32c1662d 100644
--- a/resources/views/docs/mobile/4/edge-components/text-input.md
+++ b/resources/views/docs/mobile/4/edge-components/text-input.md
@@ -83,7 +83,7 @@ Both variants are self-closing. They do not accept children.
To keep an input visible while the keyboard is up — a chat composer, a search bar — place it in a
-[``](../super-native/layouts#keyboard-aware-bottom-content), which pins above the keyboard
+[``](../the-basics/layouts#keyboard-aware-bottom-content), which pins above the keyboard
automatically. Avoid manually padding or shifting the screen yourself.
diff --git a/resources/views/docs/mobile/4/edge-components/top-bar.md b/resources/views/docs/mobile/4/edge-components/top-bar.md
index 489e7ec9..f5f9ac57 100644
--- a/resources/views/docs/mobile/4/edge-components/top-bar.md
+++ b/resources/views/docs/mobile/4/edge-components/top-bar.md
@@ -4,7 +4,7 @@ order: 450
---
> [!IMPORTANT]
-> **Prefer the [Layout model](../super-native/layouts).** Declare your app's top bar with the `NavBar`
+> **Prefer the [Layout model](../the-basics/layouts).** Declare your app's top bar with the `NavBar`
> builder in a `NativeLayout` class rather than placing `` in a screen. This page documents the
> inline element, which still works but is no longer the recommended approach.
@@ -65,7 +65,7 @@ On iOS, if more than 5 actions are provided, they collapse into an overflow menu
### `` Props
> [!IMPORTANT]
-> In the [Layout model](../super-native/layouts#builder-reference) a top-bar action is a `NavAction` — use that
+> In the [Layout model](../the-basics/layouts#builder-reference) a top-bar action is a `NavAction` — use that
> builder rather than placing `` inline.
- `id` - Unique identifier (required)
@@ -81,7 +81,7 @@ Any `url` that doesn't match a registered native route will exit to the web view
## Builder API
-When a `` is supplied by a [layout](../super-native/layouts), you build it fluently with the `NavBar`
+When a `` is supplied by a [layout](../the-basics/layouts), you build it fluently with the `NavBar`
and `NavAction` builders rather than writing it in Blade.
```php
@@ -144,4 +144,4 @@ class ItemDetail extends NativeComponent
}
```
-See [Layouts](../super-native/layouts) for the full picture.
+See [Layouts](../the-basics/layouts) for the full picture.
diff --git a/resources/views/docs/mobile/4/edge-components/web-view.md b/resources/views/docs/mobile/4/edge-components/web-view.md
index ab99c53c..08eb749e 100644
--- a/resources/views/docs/mobile/4/edge-components/web-view.md
+++ b/resources/views/docs/mobile/4/edge-components/web-view.md
@@ -188,7 +188,7 @@ Older Android versions ship with older WebView engines that may not support mode
For example, Tailwind CSS v4 uses `@theme` and other newer CSS features that are not supported on older WebView
versions. If you are targeting a lower `min_sdk` to support older devices, consider using Tailwind CSS v3 or another
CSS framework that generates compatible output. You can configure your minimum SDK version in your
-[Android SDK Versions](/docs/mobile/3/getting-started/configuration#android-sdk-versions) settings.
+[Android SDK Versions](../getting-started/configuration#android-sdk-versions) settings.
Always test your app on emulators running your minimum supported Android version to catch these issues early. You can
create emulators for older API levels in Android Studio's Virtual Device Manager.
diff --git a/resources/views/docs/mobile/4/getting-started/changelog.md b/resources/views/docs/mobile/4/getting-started/changelog.md
index 2ce7b918..5f3026d0 100644
--- a/resources/views/docs/mobile/4/getting-started/changelog.md
+++ b/resources/views/docs/mobile/4/getting-started/changelog.md
@@ -18,12 +18,12 @@ For changes prior to v4, see the [v3 documentation](/docs/mobile/3/getting-start
### New Features
-- **[SuperNative](../super-native/introduction)** — fully native UI powered by SwiftUI on iOS and Jetpack Compose on Android, and the new default for v4 apps
+- **[SuperNative](../architecture/super-native)** — fully native UI powered by SwiftUI on iOS and Jetpack Compose on Android, and the new default for v4 apps
- **Livewire-like native components** — each screen is a PHP `NativeComponent` class holding its state and behavior, re-rendering the native UI as your properties change
- **Shared memory between PHP and the native layer** — no serialization overhead or web view bridge between your code and the UI
- **[Web view as a component](../edge-components/web-view)** — the classic web-view-first approach is now opt-out: render a single native route with a full-screen web view to keep building the v3 way
-- **[Layouts](../super-native/layouts)** — declare shared chrome (nav bars, tab bars) once in a `NativeLayout` class and attach it to a route or group of routes
-- **[Native navigation stack](../super-native/navigation)** — register screens with `Route::native()`, then push, pop, and replace them with native transitions
+- **[Layouts](../the-basics/layouts)** — declare shared chrome (nav bars, tab bars) once in a `NativeLayout` class and attach it to a route or group of routes
+- **[Native navigation stack](../the-basics/routing)** — register screens with `Route::native()`, then push, pop, and replace them with native transitions
- **[Component testing suite](../testing/introduction)** — mount a `NativeComponent`, drive interactions, and assert on state and output entirely in-process, with no device or simulator
### Breaking Changes
diff --git a/resources/views/docs/mobile/4/getting-started/commands.md b/resources/views/docs/mobile/4/getting-started/commands.md
index 8b8f0e0e..992ffcad 100644
--- a/resources/views/docs/mobile/4/getting-started/commands.md
+++ b/resources/views/docs/mobile/4/getting-started/commands.md
@@ -65,7 +65,7 @@ php artisan native:watch {platform?} {target?}
-As of **v4**, the Vite dev server is **opt-in** — pass `--vite` to `native:run`/`native:watch` to start it. It
+The Vite dev server is **opt-in** — pass `--vite` to `native:run`/`native:watch` to start it. It
was previously started automatically; the old `--no-vite` flag still exists but is now redundant. See the
[Upgrade Guide](upgrade-guide).
diff --git a/resources/views/docs/mobile/4/getting-started/configuration.md b/resources/views/docs/mobile/4/getting-started/configuration.md
index 5cc588c6..a5c0c66f 100644
--- a/resources/views/docs/mobile/4/getting-started/configuration.md
+++ b/resources/views/docs/mobile/4/getting-started/configuration.md
@@ -47,7 +47,7 @@ latest code without needing to bump a version number by hand.
You only need to change this when you're cutting a release build for distribution. Rather than editing your `.env`
manually, use the
-[`native:release` command](/docs/mobile/3/getting-started/commands#nativerelease), which bumps the version (and build
+[`native:release` command](commands#nativerelease), which bumps the version (and build
number) for you.
@@ -68,7 +68,7 @@ you see your latest changes.
## Persistent Runtime
-v3.1 introduces a persistent PHP runtime that boots Laravel once and reuses the kernel across all subsequent requests.
+NativePHP uses a persistent PHP runtime that boots Laravel once and reuses the kernel across all subsequent requests.
This dramatically improves performance — from ~200-300ms per request down to ~5-30ms.
The `runtime` section controls this behavior:
@@ -106,7 +106,7 @@ Configure deep linking to allow URLs to open your app directly:
The `deeplink_scheme` enables custom URL schemes (e.g. `myapp://some/path`), while `deeplink_host` enables
verified HTTPS links and NFC tags (e.g. `https://your-host.com/path`).
-See the [Deep Links](../concepts/deep-links) documentation for full details.
+See the [Deep Links](../digging-deeper/deep-links) documentation for full details.
## Start URL
@@ -131,7 +131,7 @@ to function and bloat your downloads.
## Orientation
-NativePHP (as of v1.10.3) allows users to custom specific orientations per device through the config file. The config
+NativePHP allows users to custom specific orientations per device through the config file. The config
allows for granularity for iPad, iPhone and Android devices. Options for each device can be seen below.
NOTE: if you want to disable iPad support completely simply apply `false` for each option.
diff --git a/resources/views/docs/mobile/4/getting-started/deployment.md b/resources/views/docs/mobile/4/getting-started/deployment.md
deleted file mode 100644
index b5742691..00000000
--- a/resources/views/docs/mobile/4/getting-started/deployment.md
+++ /dev/null
@@ -1,514 +0,0 @@
----
-title: Deployment
-order: 300
----
-
-Deploying mobile apps is a complicated process — and it's different for each platform!
-
-
-
-#### There's an Easier Way
-
-Managing certificates, provisioning profiles, keystores, and coordinating deployments across teams can be frustrating
-and time-consuming.
-
-[Bifrost](https://bifrost.nativephp.com) handles all of this complexity for you.
-
-- **Set credentials once per app** - No more managing certificates and profiles locally
-- **Team collaboration** - Share apps with your team and manage access easily
-- **Auto-deploy** - Push updates automatically without manual builds
-- **Over-the-air updates** - Deploy changes to users instantly without app store approval
-- **Low monthly cost** - Simple, affordable pricing
-
-
-
-Generally speaking you need to:
-
-1. **Releasing**: Create a _release build_ for each platform.
-2. **Testing**: Test this build on real devices.
-3. **Packaging**: Sign and distribute this build to the stores.
-4. **Submitting for Review**: Go through each store's submission process to have your app reviewed.
-5. **Publishing**: Releasing the new version to the stores and your users.
-
-It's initially more time-consuming when creating a brand new app in the stores, as you need to get the listing set up
-in each store and create your signing credentials.
-
-If you've never done it before, allow a couple of hours so you can focus on getting things right and understand
-everything you need.
-
-Don't rush through the app store processes! There are compliance items that if handled incorrectly will either prevent
-you from publishing your app, being unable to release it in the territories you want to make it available to, or simply
-having it get rejected immediately when you submit it for review if you don't get those right.
-
-It's typically easier once you've released the first version of your app and after you've done 2 or 3 apps, you'll fly
-through the process!
-
-
-
-#### Need help?
-
-This page is here to help you _configure and use NativePHP_ to prepare your app for release; it is not a guide around
-the stores. You should consult the [App Store Connect Help](https://developer.apple.com/help/app-store-connect/) and
-[Play Console Help](https://support.google.com/googleplay/android-developer/?hl=en-GB#topic=3450769) documentation for
-detailed and up-to-date guidance on how to prepare your app submissions and listings.
-
-If you want more hands-on support, we happily work with our [Partners](/partners) to support them releasing their apps.
-
-
-
-## Releasing
-
-To prepare your app for release, bump the version number using the
-[`native:release` command](/docs/mobile/3/getting-started/commands#nativerelease):
-
-```shell
-php artisan native:release patch
-```
-
-You can pass `patch`, `minor`, or `major` depending on the type of release you're cutting. This updates
-`NATIVEPHP_APP_VERSION` in your `.env` and increments the build number for you.
-
-### Versioning
-
-App version numbers should follow [semantic versioning](https://semver.org) (e.g. `1.2.3`). The `native:release` command
-relies on this format to determine how to bump your version.
-
-Remember that your app versions are usually public-facing (e.g. in store listings and on-device settings and update
-screens) and can be useful for customers to reference if they need to contact you for help and support.
-
-The app version is managed via the `NATIVEPHP_APP_VERSION` key in your `.env`.
-
-### Build numbers
-
-Both the Google Play Store and Apple App Store require your app's build number to increase for each release you submit.
-
-The build number is managed via the `NATIVEPHP_APP_VERSION_CODE` key in your `.env`. You don't need to manage this
-yourself — running `native:release` automatically increments the build number and persists it back to your `.env`.
-
-### Run a `release` build
-
-Then run a release build:
-
-```shell
-php artisan native:run --build=release
-```
-
-This builds your application with various optimizations that reduce its overall size and improve its performance, such
-as removing debugging code and unnecessary features (i.e. Composer dev dependencies).
-
-**You should test this build on a real device.** Once you're happy that everything is working as intended you can then
-submit it to the stores for approval and distribution.
-
-- [Google Play Store submission guidelines](https://support.google.com/googleplay/android-developer/answer/9859152?hl=en-GB#zippy=%2Cmaximum-size-limit)
-- [Apple App Store submission guidelines](https://developer.apple.com/ios/submit/)
-
-## Packaging Your App
-
-The `native:package` command creates signed, production-ready apps for distribution to the App Store and Play Store.
-This command handles all the complexity of code signing, building release artifacts, and preparing files for submission.
-
-## Before You Begin
-
-Before you can package your app for distribution, ensure:
-
-1. Your app is fully developed and tested on both platforms
-2. You have a valid bundle ID and app ID configured in your `nativephp.php` config
-3. For Android: You have a signing keystore with a valid key alias
-4. For iOS: You have the necessary signing certificates and provisioning profiles from Apple Developer
-5. All configuration is complete (see the [configuration guide](/docs/mobile/3/getting-started/configuration))
-
-## Android Packaging
-
-### Creating Android Signing Credentials
-
-NativePHP provides a convenient command to generate all the signing credentials you need for Android:
-
-```bash
-php artisan native:credentials android
-```
-
-This command will:
-- Generate a new JKS keystore file
-- Create all necessary signing keys
-- Automatically add the credentials to your `.env` file
-- Add the keystore to your `.gitignore` to keep it secure
-
-The credentials will be saved in the `nativephp/credentials/android/` directory and automatically configured for use with the package command.
-
-### Required Signing Credentials
-
-To build a signed Android app, you need four pieces of information:
-
-| Credential | Option | Environment Variable | Description |
-|-----------|--------|----------------------|-------------|
-| Keystore file | `--keystore` | `ANDROID_KEYSTORE_FILE` | Path to your `.keystore` file |
-| Keystore password | `--keystore-password` | `ANDROID_KEYSTORE_PASSWORD` | Password for the keystore |
-| Key alias | `--key-alias` | `ANDROID_KEY_ALIAS` | Name of the key within the keystore |
-| Key password | `--key-password` | `ANDROID_KEY_PASSWORD` | Password for the specific key |
-
-### Building a Release APK
-
-An APK (Android Package) is a single binary file suitable for direct distribution or testing on specific devices:
-
-```bash
-php artisan native:package android \
- --keystore=/path/to/my-app.keystore \
- --keystore-password=mykeystorepassword \
- --key-alias=my-app-key \
- --key-password=mykeypassword
-```
-
-The build process prepares your Android project, compiles the code, and signs the APK with your certificate. When complete, the output directory opens automatically, showing your signed `app-release.apk` file.
-
-### Building an Android App Bundle (AAB)
-
-An AAB (Android App Bundle) is required for distribution through the Play Store. It's an optimized format that the Play Store uses to generate device-specific APKs automatically:
-
-```bash
-php artisan native:package android \
- --build-type=bundle \
- --keystore=/path/to/my-app.keystore \
- --keystore-password=mykeystorepassword \
- --key-alias=my-app-key \
- --key-password=mykeypassword
-```
-
-This creates a signed `app-release.aab` file ready for Play Store submission.
-
-### Using Environment Variables
-
-Instead of passing credentials as command options, you can store them in your `.env` file:
-
-```env
-ANDROID_KEYSTORE_FILE=/path/to/my-app.keystore
-ANDROID_KEYSTORE_PASSWORD=mykeystorepassword
-ANDROID_KEY_ALIAS=my-app-key
-ANDROID_KEY_PASSWORD=mykeypassword
-```
-
-Then simply run:
-
-```bash
-php artisan native:package android --build-type=bundle
-```
-
-### Uploading to Play Store
-
-If you have a Google Service Account with Play Console access, you can upload directly from the command:
-
-```bash
-php artisan native:package android \
- --build-type=bundle \
- --keystore=/path/to/my-app.keystore \
- --keystore-password=mykeystorepassword \
- --key-alias=my-app-key \
- --key-password=mykeypassword \
- --upload-to-play-store \
- --play-store-track=internal \
- --google-service-key=/path/to/service-account-key.json
-```
-
-The `--play-store-track` option controls where the build is released:
-
-- `internal` - Internal testing (default, fastest review)
-- `alpha` - Closed alpha testing
-- `beta` - Closed beta testing
-- `production` - Production release
-
-### Testing Play Store Uploads
-
-If you already have an AAB file and want to test uploading without rebuilding, use `--test-push`:
-
-```bash
-php artisan native:package android \
- --test-push=/path/to/app-release.aab \
- --upload-to-play-store \
- --play-store-track=internal \
- --google-service-key=/path/to/service-account-key.json
-```
-
-This skips the entire build process and only handles the upload.
-
-### Skipping Build Preparation
-
-For incremental builds where you haven't changed native code, you can skip the preparation phase:
-
-```bash
-php artisan native:package android \
- --build-type=bundle \
- --keystore=/path/to/my-app.keystore \
- --keystore-password=mykeystorepassword \
- --key-alias=my-app-key \
- --key-password=mykeypassword \
- --skip-prepare
-```
-
-## iOS Packaging
-
-### Required Signing Credentials
-
-iOS apps require several credentials from Apple Developer:
-
-| Credential | Option | Environment Variable | Description |
-|-----------|--------|----------------------|-------------|
-| API Key file | `--api-key-path` | `APP_STORE_API_KEY_PATH` | Path to `.p8` file from App Store Connect |
-| API Key ID | `--api-key-id` | `APP_STORE_API_KEY_ID` | Key ID from App Store Connect |
-| API Issuer ID | `--api-issuer-id` | `APP_STORE_API_ISSUER_ID` | Issuer ID from App Store Connect |
-| Certificate | `--certificate-path` | `IOS_DISTRIBUTION_CERTIFICATE_PATH` | Distribution certificate (`.p12` or `.cer`) |
-| Certificate password | `--certificate-password` | `IOS_DISTRIBUTION_CERTIFICATE_PASSWORD` | Password for the certificate |
-| Provisioning profile | `--provisioning-profile-path` | `IOS_DISTRIBUTION_PROVISIONING_PROFILE_PATH` | Profile file (`.mobileprovision`) |
-| Team ID | `--team-id` | `IOS_TEAM_ID` | Apple Developer Team ID |
-
-### Setting Up App Store Connect API
-
-To upload to the App Store directly from the command line, you'll need an API key:
-
-1. Log in to [App Store Connect](https://appstoreconnect.apple.com)
-2. Navigate to Users & Access → Keys
-3. Click the "+" button to create a new key with "Developer" access
-4. Download the `.p8` file immediately (you can't download it again later)
-5. Note the Key ID and Issuer ID displayed on the page
-
-### Export Methods
-
-The `--export-method` option controls how your app is packaged:
-
-- `app-store` - For App Store distribution (default)
-- `ad-hoc` - For distribution to specific registered devices
-- `enterprise` - For enterprise distribution (requires enterprise program)
-- `development` - For development and testing
-
-### Building for App Store
-
-To build a production app ready for App Store submission:
-
-```bash
-php artisan native:package ios \
- --export-method=app-store \
- --api-key-path=/path/to/api-key.p8 \
- --api-key-id=ABC123DEF \
- --api-issuer-id=01234567-89ab-cdef-0123-456789abcdef \
- --certificate-path=/path/to/distribution.p12 \
- --certificate-password=certificatepassword \
- --provisioning-profile-path=/path/to/profile.mobileprovision \
- --team-id=ABC1234567
-```
-
-### Building for Ad-Hoc Distribution
-
-For distributing to specific devices without going through the App Store:
-
-```bash
-php artisan native:package ios \
- --export-method=ad-hoc \
- --certificate-path=/path/to/distribution.p12 \
- --certificate-password=certificatepassword \
- --provisioning-profile-path=/path/to/ad-hoc-profile.mobileprovision
-```
-
-### Building for Development
-
-For testing on your own device:
-
-```bash
-php artisan native:package ios \
- --export-method=development \
- --certificate-path=/path/to/development.p12 \
- --certificate-password=certificatepassword \
- --provisioning-profile-path=/path/to/development-profile.mobileprovision
-```
-
-### Using Environment Variables
-
-Store your iOS credentials in `.env`:
-
-```env
-APP_STORE_API_KEY_PATH=/path/to/api-key.p8
-APP_STORE_API_KEY_ID=ABC123DEF
-APP_STORE_API_ISSUER_ID=01234567-89ab-cdef-0123-456789abcdef
-IOS_DISTRIBUTION_CERTIFICATE_PATH=/path/to/distribution.p12
-IOS_DISTRIBUTION_CERTIFICATE_PASSWORD=certificatepassword
-IOS_DISTRIBUTION_PROVISIONING_PROFILE_PATH=/path/to/profile.mobileprovision
-IOS_TEAM_ID=ABC1234567
-```
-
-Then build with:
-
-```bash
-php artisan native:package ios --export-method=app-store
-```
-
-### Uploading to App Store Connect
-
-To automatically upload after building:
-
-```bash
-php artisan native:package ios \
- --export-method=app-store \
- --api-key-path=/path/to/api-key.p8 \
- --api-key-id=ABC123DEF \
- --api-issuer-id=01234567-89ab-cdef-0123-456789abcdef \
- --certificate-path=/path/to/distribution.p12 \
- --certificate-password=certificatepassword \
- --provisioning-profile-path=/path/to/profile.mobileprovision \
- --team-id=ABC1234567 \
- --upload-to-app-store
-```
-
-The upload uses the App Store Connect API, so API credentials are required only when using `--upload-to-app-store`.
-
-### Validating Provisioning Profiles
-
-Before building, you can validate your provisioning profile to check push notification support and entitlements:
-
-```bash
-php artisan native:package ios \
- --validate-profile \
- --provisioning-profile-path=/path/to/profile.mobileprovision
-```
-
-This extracts and displays:
-
-- Profile name
-- All entitlements configured in the profile
-- Push notification support status
-- Associated domains
-- APS environment matching
-
-### Testing App Store Uploads
-
-To test uploading an existing IPA without rebuilding:
-
-```bash
-php artisan native:package ios \
- --test-upload \
- --api-key-path=/path/to/api-key.p8 \
- --api-key-id=ABC123DEF \
- --api-issuer-id=01234567-89ab-cdef-0123-456789abcdef
-```
-
-### Clearing Xcode Caches
-
-If you encounter build issues, clear Xcode and Swift Package Manager caches:
-
-```bash
-php artisan native:package ios \
- --export-method=app-store \
- --clean-caches
-```
-
-### Forcing a Clean Rebuild
-
-To force a complete rebuild and create a new archive:
-
-```bash
-php artisan native:package ios \
- --export-method=app-store \
- --rebuild
-```
-
-### Validating Without Exporting
-
-To validate the archive without creating an IPA:
-
-```bash
-php artisan native:package ios \
- --validate-only
-```
-
-## Version Management
-
-### Build Numbers and Version Codes
-
-The `native:version` command handles version management. When building AABs for the Play Store with valid Google Service credentials, NativePHP automatically checks the Play Store for the latest build number and increments it.
-
-### Auto-Incrementing from Play Store
-
-When building a bundle with Play Store access:
-
-```bash
-php artisan native:package android \
- --build-type=bundle \
- --keystore=/path/to/my-app.keystore \
- --keystore-password=mykeystorepassword \
- --key-alias=my-app-key \
- --key-password=mykeypassword \
- --google-service-key=/path/to/service-account-key.json
-```
-
-The command automatically queries the Play Store to find your latest published build number and increments it.
-
-### Jumping Ahead in Version Numbers
-
-If you need to skip version numbers or jump ahead:
-
-```bash
-php artisan native:package android \
- --build-type=bundle \
- --keystore=/path/to/my-app.keystore \
- --keystore-password=mykeystorepassword \
- --key-alias=my-app-key \
- --key-password=mykeypassword \
- --jump-by=10
-```
-
-This adds 10 to the version code that would normally be used.
-
-## Tips & Troubleshooting
-
-### Common Android Issues
-
-**Keystore-related errors:**
-- Verify the keystore file exists and the path is correct
-- Check that the keystore password is correct
-- Confirm the key alias exists in the keystore with: `keytool -list -v -keystore /path/to/keystore`
-- Verify the key password matches
-
-**Build failures:**
-- Ensure you have the latest Android SDK and build tools installed
-- Check that your `nativephp/android` directory exists and is properly initialized
-- If you've modified native code, don't use `--skip-prepare`
-
-**Play Store upload failures:**
-- Verify the Google Service Account has access to your app in Play Console
-- Ensure the service account key file is valid and readable
-- Check that your bundle ID matches your Play Console app ID
-
-### Custom Output Directories
-
-By default, the build output opens in your system's file manager. To copy the artifact to a custom location:
-
-```bash
-php artisan native:package android \
- --build-type=bundle \
- --keystore=/path/to/my-app.keystore \
- --keystore-password=mykeystorepassword \
- --key-alias=my-app-key \
- --key-password=mykeypassword \
- --output=/path/to/custom/directory
-```
-
-### Building in Non-Interactive Environments
-
-For CI/CD pipelines or automated builds, disable TTY mode:
-
-```bash
-php artisan native:package android \
- --build-type=bundle \
- --keystore=/path/to/my-app.keystore \
- --keystore-password=mykeystorepassword \
- --key-alias=my-app-key \
- --key-password=mykeypassword \
- --no-tty
-```
-
-### Artifact Locations
-
-Once complete, signed artifacts are located at:
-
-**Android:**
-- APK (release): `nativephp/android/app/build/outputs/apk/release/app-release.apk`
-- AAB (bundle): `nativephp/android/app/build/outputs/bundle/release/app-release.aab`
-
-**iOS:**
-- IPA: Generated in Xcode's build output directory
diff --git a/resources/views/docs/mobile/4/getting-started/development.md b/resources/views/docs/mobile/4/getting-started/development.md
index bfd44ee3..3e854253 100644
--- a/resources/views/docs/mobile/4/getting-started/development.md
+++ b/resources/views/docs/mobile/4/getting-started/development.md
@@ -189,7 +189,7 @@ php artisan native:run --watch --vite
-As of **v4**, the Vite dev server is **opt-in** — it only starts when you pass `--vite`. In earlier versions it
+The Vite dev server is **opt-in** — it only starts when you pass `--vite`. It previously
started automatically and you disabled it with `--no-vite` (that flag still exists but is now redundant).
diff --git a/resources/views/docs/mobile/4/getting-started/introduction.md b/resources/views/docs/mobile/4/getting-started/introduction.md
index 15a2bf6a..9fdc0453 100644
--- a/resources/views/docs/mobile/4/getting-started/introduction.md
+++ b/resources/views/docs/mobile/4/getting-started/introduction.md
@@ -43,8 +43,7 @@ ever. In just a few minutes, you can go from code to app store submission.
1. A pre-compiled version of PHP is bundled with your code into a Swift/Kotlin shell application.
2. NativePHP's custom Swift/Kotlin bridges manage the PHP environment, running your PHP code directly.
3. A custom PHP extension is compiled into PHP, that exposes PHP interfaces to native functions.
-4. Build with HTML, JavaScript, Tailwind, Blade, Livewire, React, Vue, Svelte — whatever you're most comfortable with!
-5. And now in v3: use truly native UI components too with [EDGE](/docs/mobile/3/edge-components/)!
+4. Build truly native UI with [EDGE](../edge-components/introduction) components — Blade components that render as real native views, so it feels just like building with Livewire. Read more about how it works in the [Architecture](../architecture/super-native) section.
You simply interact with an easy-to-use set of functions from PHP and everything just works!
@@ -53,10 +52,15 @@ You simply interact with an easy-to-use set of functions from PHP and everything
NativePHP for Mobile is way more than just a web view wrapper for your server-based application. Your application lives
_on device_ and is shipped with each installation.
-Thanks to our custom PHP extension, you can interact with many native APIs today, with more coming all the time. Check out the API documentation section to see everything that's available.
+It's not a bare-bones package that leaves you assembling an array of third-party dependencies just to build your app —
+a great deal comes included. At the same time, the shell applications intentionally _don't_ bundle everything you might
+need: keeping them lean means a smaller app footprint, shorter build times, and minimal permission requirements — all of
+which smooths app store review and improves adoption.
-You have the full power of PHP and Laravel at your fingertips... literally! And you're not sandboxed into the web view;
-this goes way beyond what's possible with PWAs and WASM without any of the complexity... we've got full-cream PHP at
-the ready!
+Thanks to our custom PHP extension, you can interact with many native APIs today, with more coming all the time. Check out the [Plugins](../plugins/introduction) section to see everything that's available.
+
+You have the full power of PHP and Laravel at your fingertips... literally! And you're not sandboxed into a web view —
+NativePHP for Mobile renders full native UI by default, going way beyond what's possible with PWAs and WASM without any
+of the complexity... this is full-fat, full-stack PHP at the ready!
**What are you waiting for!? [Let's go!](quick-start)**
diff --git a/resources/views/docs/mobile/4/getting-started/roadmap.md b/resources/views/docs/mobile/4/getting-started/roadmap.md
deleted file mode 100644
index c5d347f2..00000000
--- a/resources/views/docs/mobile/4/getting-started/roadmap.md
+++ /dev/null
@@ -1,32 +0,0 @@
----
-title: Roadmap
-order: 400
----
-
-NativePHP for Mobile is stable and already deployed in production apps released on the app stores. But we're not done
-yet! Here's what we're focusing on next:
-
-## Background tasks
-
-We will be adding the ability to run code in the background, even when your app isn't in the foreground. Perfect for
-syncing data, processing uploads, or handling push notifications.
-
-## Native UI through EDGE
-
-We will be expanding EDGE's (Element Definition and Generation Engine) capabilities, letting you define more truly
-native UI components from your PHP code. Build navigation bars, tab bars, and other native elements that feel right at
-home on each platform.
-
-## Performance
-
-We will be improving NativePHP's performance, making apps faster and more efficient. Expect improvements to startup
-time, memory usage, and overall responsiveness.
-
-
-
-#### Something missing?
-
-With NativePHP's [plugin system](/plugins), the community can build interfaces to any native functionality. If there's
-something you need that isn't available yet, you can build it yourself or find a plugin that does what you need.
-
-
diff --git a/resources/views/docs/mobile/4/getting-started/upgrade-guide.md b/resources/views/docs/mobile/4/getting-started/upgrade-guide.md
index 9fd2cbe2..8e493013 100644
--- a/resources/views/docs/mobile/4/getting-started/upgrade-guide.md
+++ b/resources/views/docs/mobile/4/getting-started/upgrade-guide.md
@@ -5,28 +5,10 @@ order: 3
## Upgrading To 4.0 From 3.x
-v4's headline is [SuperNative](../super-native/introduction) — fully native UI. Most of the release is additive,
+v4's headline is [SuperNative](../architecture/super-native) — fully native UI. Most of the release is additive,
but there is **one breaking change to your dependencies**: a handful of APIs that used to be separate plugins are
now core built-ins.
-### Why SuperNative
-
-For its first three major versions, NativePHP for Mobile was built around a fast, opinionated web view. It worked
-well — but it tied your app's UI to a stack of moving parts we didn't own, and every upstream release was a chance
-for something to break. Inertia 3 dropped its axios dependency and broke Inertia apps. Livewire 4 began emitting
-filenames containing an emoji (🔥) that our bundled PHP binaries initially couldn't read. We spent real energy
-chasing a target that kept moving, on layers we couldn't control.
-
-SuperNative changes that equation. Instead of rendering your UI in a browser and hoping the layers above stay
-compatible, it renders real SwiftUI and Jetpack Compose views driven directly by your PHP — a stack we own end to
-end, from your Laravel app all the way down to the native view tree. That means our effort goes into making *that*
-fast, stable, and capable, rather than reacting to churn elsewhere. The web view's advantages have narrowed as
-SuperNative's rendering has matured, and for new work SuperNative is where the platform — and our focus — is
-headed.
-
-The web view isn't going away: [it's still available as a component](../edge-components/web-view) for the cases
-that genuinely need HTML. But it's now opt-in, not the foundation.
-
### Device, Dialog, File and System are now built in
`Device`, `Dialog`, `File`, and `System` now ship inside `nativephp/mobile` — their native bridge functions are
@@ -49,9 +31,9 @@ composer remove nativephp/mobile-device nativephp/mobile-dialog nativephp/mobile
```
**No application code changes are required.** The `Native\Mobile\Facades\{Device, Dialog, File, System}` facades
-and their events (`ButtonPressed`, etc.) are unchanged. Their docs now live in the SuperNative section:
-[Device](../super-native/device), [Dialog](../super-native/dialog), [File](../super-native/file), and
-[System](../super-native/system).
+and their events (`ButtonPressed`, etc.) are unchanged. Their docs now live in The Basics section:
+[Device](../the-basics/device), [Dialog](../the-basics/dialogs), [File](../the-basics/file), and
+[System](../the-basics/system).
### The Vite dev server is now opt-in
@@ -86,179 +68,15 @@ The `--force` flag rebuilds the native project files with the v4 versions.
+### For Plugin Developers
-## Upgrading To 3.1 From 3.0
-
-v3.1 is a drop-in upgrade with no breaking changes. The headline feature is a **persistent PHP runtime** that
-dramatically improves request performance.
-
-
-### Update your dependency
-
-```json
-"require": {
- "nativephp/mobile": "~3.0.0" // [tl! remove]
- "nativephp/mobile": "~3.1.0" // [tl! add]
-}
-```
-
-```sh
-composer update
-php artisan native:install --force
-```
-
-
-
-The `--force` flag is important — it ensures the native project files, PHP binaries, and config are all updated
-to the v3.1 versions.
-
-
-
-
-## Android 8+ Support
-
-In order to support Android API 26 add the following to your `config/nativephp.php`:
-
-```php
-'android' => [
- 'compile_sdk' => env('NATIVEPHP_ANDROID_COMPILE_SDK', 36),
- 'min_sdk' => env('NATIVEPHP_ANDROID_MIN_SDK', 33),
- 'target_sdk' => env('NATIVEPHP_ANDROID_TARGET_SDK', 36),
-],
-```
-
-No code changes are required — this is handled entirely at the build level.
-
-## ICU/Intl Support on iOS
-
-iOS builds now include full **ICU support**, which means the PHP `intl` extension works on both platforms.
-This was previously only available on Android.
-
-This is a big deal — packages like [Filament](https://filamentphp.com) depend on `intl` for number formatting,
-date formatting, and pluralization. With v3.1, **Filament works on both iOS and Android** out of the box.
-
-ICU support remains optional via the `--with-icu` / `--without-icu` flags during installation it adds ~30MB to your app on Android and ~100MB on iOS.
----
-
-## Upgrading To 3.0 From 2.x
-
-NativePHP for Mobile v3 introduces a plugin-based architecture that makes the entire native layer extensible.
-All core functionality continues to work as before, but the underlying system is now modular and open to
-third-party plugins.
-
-## Remove the NativePHP Composer Repository
-
-v3 no longer requires the private Composer repository or license authentication. Remove the `nativephp.composer.sh`
-repository from your `composer.json`:
-
-```json
-"repositories": [
- {
- "type": "composer",
- "url": "https://nativephp.composer.sh"
- }
-]
-```
-
-Delete the entire `repositories` block above (or just the NativePHP entry if you have other repositories).
-
-You can also remove any stored credentials for `nativephp.composer.sh` from your `auth.json` if you have one.
-
-Then update your version constraint and run the upgrade:
+Widen the constraint in your plugin's `composer.json` to allow the new release:
```json
"require": {
- "nativephp/mobile": "~2.0.0" // [tl! remove]
- "nativephp/mobile": "~3.0.0" // [tl! add]
+ "nativephp/mobile": "^3.0" // [tl! remove]
+ "nativephp/mobile": "^3.0|^4.0" // [tl! add]
}
```
-```sh
-composer update
-php artisan native:install --force
-```
-
-## Plugin Architecture
-
-v3 introduces a comprehensive plugin system. Native functionality is now delivered through plugins — including all
-the official core APIs you already use (Camera, Biometrics, Scanner, etc.). These continue to work exactly as before;
-you don't need to change how you call them.
-
-The difference is that **third-party developers can now create plugins** that add new native functionality to your
-app. Plugins are standard Composer packages that include Swift (iOS) and Kotlin (Android) code alongside their
-PHP interface.
-
-Read more about the plugin system in the [Plugins documentation](../plugins/introduction), or browse
-ready-made plugins on the [NativePHP Plugin Marketplace](https://nativephp.com/plugins).
-
-## NativeServiceProvider
-
-v3 introduces a `NativeServiceProvider` for registering third-party plugins. Publish it with:
-
-```shell
-php artisan vendor:publish --tag=nativephp-plugins-provider
-```
-
-This creates `app/Providers/NativeServiceProvider.php`. Any third-party plugins you install must be registered
-here before their native code is compiled into your app. This is a security measure to prevent transitive
-dependencies from automatically including native code without your consent.
-
-```shell
-php artisan native:plugin:register vendor/some-plugin
-```
-
-Core APIs provided by `nativephp/mobile` do not need to be registered manually — they are included automatically.
-
-Read more about [Using Plugins](../plugins/using-plugins).
-
-## Core APIs Are Now Plugins
-
-All core APIs (Camera, Biometrics, Dialog, Scanner, Geolocation, etc.) are now implemented as plugins internally.
-The PHP facades and events you use remain the same — no changes to your application code are needed.
-
-Browse the full list of available core plugins in the [Plugins documentation](../plugins/introduction).
-
-## Plugin Management Commands
-
-v3 adds several new Artisan commands for working with plugins:
-
-| Command | Description |
-|---------|-------------|
-| `native:plugin:create` | Scaffold a new plugin |
-| `native:plugin:register` | Register a plugin in your NativeServiceProvider |
-| `native:plugin:list` | List installed plugins |
-| `native:plugin:uninstall` | Remove a plugin |
-| `native:plugin:validate` | Validate plugin structure |
-| `native:plugin:make-hook` | Create a lifecycle hook |
-
-## Bridge Functions
-
-Plugins communicate with native code through **bridge functions** — a standardized pattern for calling
-Swift and Kotlin code from PHP via `nativephp_call()`. Each plugin declares its bridge functions in a
-`nativephp.json` manifest.
-
-If you've been using the core APIs through their facades, nothing changes for you. Bridge functions are primarily
-relevant if you're building your own plugins.
-
-Read more in the [Bridge Functions documentation](../plugins/bridge-functions).
-
-## Plugin Marketplace
-
-Find ready-made plugins for common use cases, or get the Dev Kit to build your own.
-[Visit the NativePHP Plugin Marketplace](https://nativephp.com/plugins).
-
-## Command Reference
-
-v3 includes a comprehensive [Command Reference](commands) documenting all `native:*` Artisan commands
-with their options and usage.
-
-## Rebuild Required
-
-After upgrading, you must rebuild your native application:
-
-```shell
-php artisan native:install --force
-php artisan native:run
-```
-
-The `--force` flag ensures the `nativephp` directory is completely rebuilt with the v3 native project files.
+If your plugin depends on any of the conflicting plugins above, remove them from your `composer.json`'s `require`.
diff --git a/resources/views/docs/mobile/4/plugins/best-practices.md b/resources/views/docs/mobile/4/plugins/best-practices.md
index 2d3f942d..720136d7 100644
--- a/resources/views/docs/mobile/4/plugins/best-practices.md
+++ b/resources/views/docs/mobile/4/plugins/best-practices.md
@@ -273,10 +273,10 @@ developers can reach you with questions or issues. You can update this anytime f
### Additional Checks
**iOS native code** — Your plugin must include native Swift code in `resources/ios/Sources/`. See
-[Bridge Functions](/docs/mobile/3/plugins/bridge-functions) for the implementation pattern.
+[Bridge Functions](bridge-functions) for the implementation pattern.
**Android native code** — Your plugin must include native Kotlin code in `resources/android/src/`. See
-[Bridge Functions](/docs/mobile/3/plugins/bridge-functions) for the implementation pattern.
+[Bridge Functions](bridge-functions) for the implementation pattern.
**JavaScript library** — Your plugin must include a JavaScript library in `resources/js/` that exports
a function for every bridge function. This allows Inertia + Vue/React developers to call your native functions
@@ -294,7 +294,7 @@ your plugin is properly integrated with the NativePHP build pipeline:
```
**iOS `min_version`** — Your `nativephp.json` must specify a minimum iOS version. See
-[Advanced Configuration](/docs/mobile/3/plugins/advanced-configuration) for details:
+[Advanced Configuration](advanced-configuration) for details:
```json
{
@@ -305,7 +305,7 @@ your plugin is properly integrated with the NativePHP build pipeline:
```
**Android `min_version`** — Your `nativephp.json` must specify a minimum Android SDK version. See
-[Advanced Configuration](/docs/mobile/3/plugins/advanced-configuration) for details:
+[Advanced Configuration](advanced-configuration) for details:
```json
{
diff --git a/resources/views/docs/mobile/4/plugins/core/vibe.md b/resources/views/docs/mobile/4/plugins/core/vibe.md
new file mode 100644
index 00000000..c754bd53
--- /dev/null
+++ b/resources/views/docs/mobile/4/plugins/core/vibe.md
@@ -0,0 +1,4 @@
+---
+title: Vibe
+order: 1500
+---
diff --git a/resources/views/docs/mobile/4/publishing/_index.md b/resources/views/docs/mobile/4/publishing/_index.md
new file mode 100644
index 00000000..c2495eb0
--- /dev/null
+++ b/resources/views/docs/mobile/4/publishing/_index.md
@@ -0,0 +1,4 @@
+---
+title: Publishing Your App
+order: 80
+---
diff --git a/resources/views/docs/mobile/4/publishing/android.md b/resources/views/docs/mobile/4/publishing/android.md
new file mode 100644
index 00000000..a36d4352
--- /dev/null
+++ b/resources/views/docs/mobile/4/publishing/android.md
@@ -0,0 +1,218 @@
+---
+title: Android
+order: 20
+---
+
+Package and sign your app for the Google Play Store. Read the [Introduction](introduction) first, and make sure
+you've cut a [release build](introduction#releasing).
+
+## Creating Signing Credentials
+
+NativePHP provides a convenient command to generate all the signing credentials you need for Android:
+
+```bash
+php artisan native:credentials android
+```
+
+This command will:
+- Generate a new JKS keystore file
+- Create all necessary signing keys
+- Automatically add the credentials to your `.env` file
+- Add the keystore to your `.gitignore` to keep it secure
+
+The credentials will be saved in the `nativephp/credentials/android/` directory and automatically configured for use with the package command.
+
+## Required Signing Credentials
+
+To build a signed Android app, you need four pieces of information:
+
+| Credential | Option | Environment Variable | Description |
+|-----------|--------|----------------------|-------------|
+| Keystore file | `--keystore` | `ANDROID_KEYSTORE_FILE` | Path to your `.keystore` file |
+| Keystore password | `--keystore-password` | `ANDROID_KEYSTORE_PASSWORD` | Password for the keystore |
+| Key alias | `--key-alias` | `ANDROID_KEY_ALIAS` | Name of the key within the keystore |
+| Key password | `--key-password` | `ANDROID_KEY_PASSWORD` | Password for the specific key |
+
+## Building a Release APK
+
+An APK (Android Package) is a single binary file suitable for direct distribution or testing on specific devices:
+
+```bash
+php artisan native:package android \
+ --keystore=/path/to/my-app.keystore \
+ --keystore-password=mykeystorepassword \
+ --key-alias=my-app-key \
+ --key-password=mykeypassword
+```
+
+The build process prepares your Android project, compiles the code, and signs the APK with your certificate. When complete, the output directory opens automatically, showing your signed `app-release.apk` file.
+
+## Building an Android App Bundle (AAB)
+
+An AAB (Android App Bundle) is required for distribution through the Play Store. It's an optimized format that the Play Store uses to generate device-specific APKs automatically:
+
+```bash
+php artisan native:package android \
+ --build-type=bundle \
+ --keystore=/path/to/my-app.keystore \
+ --keystore-password=mykeystorepassword \
+ --key-alias=my-app-key \
+ --key-password=mykeypassword
+```
+
+This creates a signed `app-release.aab` file ready for Play Store submission.
+
+## Using Environment Variables
+
+Instead of passing credentials as command options, you can store them in your `.env` file:
+
+```env
+ANDROID_KEYSTORE_FILE=/path/to/my-app.keystore
+ANDROID_KEYSTORE_PASSWORD=mykeystorepassword
+ANDROID_KEY_ALIAS=my-app-key
+ANDROID_KEY_PASSWORD=mykeypassword
+```
+
+Then simply run:
+
+```bash
+php artisan native:package android --build-type=bundle
+```
+
+## Uploading to Play Store
+
+If you have a Google Service Account with Play Console access, you can upload directly from the command:
+
+```bash
+php artisan native:package android \
+ --build-type=bundle \
+ --keystore=/path/to/my-app.keystore \
+ --keystore-password=mykeystorepassword \
+ --key-alias=my-app-key \
+ --key-password=mykeypassword \
+ --upload-to-play-store \
+ --play-store-track=internal \
+ --google-service-key=/path/to/service-account-key.json
+```
+
+The `--play-store-track` option controls where the build is released:
+
+- `internal` - Internal testing (default, fastest review)
+- `alpha` - Closed alpha testing
+- `beta` - Closed beta testing
+- `production` - Production release
+
+## Testing Play Store Uploads
+
+If you already have an AAB file and want to test uploading without rebuilding, use `--test-push`:
+
+```bash
+php artisan native:package android \
+ --test-push=/path/to/app-release.aab \
+ --upload-to-play-store \
+ --play-store-track=internal \
+ --google-service-key=/path/to/service-account-key.json
+```
+
+This skips the entire build process and only handles the upload.
+
+## Skipping Build Preparation
+
+For incremental builds where you haven't changed native code, you can skip the preparation phase:
+
+```bash
+php artisan native:package android \
+ --build-type=bundle \
+ --keystore=/path/to/my-app.keystore \
+ --keystore-password=mykeystorepassword \
+ --key-alias=my-app-key \
+ --key-password=mykeypassword \
+ --skip-prepare
+```
+
+## Auto-Incrementing Version Codes
+
+The `native:version` command handles version management. When building AABs for the Play Store with valid Google Service credentials, NativePHP automatically checks the Play Store for the latest build number and increments it.
+
+When building a bundle with Play Store access:
+
+```bash
+php artisan native:package android \
+ --build-type=bundle \
+ --keystore=/path/to/my-app.keystore \
+ --keystore-password=mykeystorepassword \
+ --key-alias=my-app-key \
+ --key-password=mykeypassword \
+ --google-service-key=/path/to/service-account-key.json
+```
+
+The command automatically queries the Play Store to find your latest published build number and increments it.
+
+## Jumping Ahead in Version Numbers
+
+If you need to skip version numbers or jump ahead:
+
+```bash
+php artisan native:package android \
+ --build-type=bundle \
+ --keystore=/path/to/my-app.keystore \
+ --keystore-password=mykeystorepassword \
+ --key-alias=my-app-key \
+ --key-password=mykeypassword \
+ --jump-by=10
+```
+
+This adds 10 to the version code that would normally be used.
+
+## Custom Output Directories
+
+By default, the build output opens in your system's file manager. To copy the artifact to a custom location:
+
+```bash
+php artisan native:package android \
+ --build-type=bundle \
+ --keystore=/path/to/my-app.keystore \
+ --keystore-password=mykeystorepassword \
+ --key-alias=my-app-key \
+ --key-password=mykeypassword \
+ --output=/path/to/custom/directory
+```
+
+## Building in Non-Interactive Environments
+
+For CI/CD pipelines or automated builds, disable TTY mode:
+
+```bash
+php artisan native:package android \
+ --build-type=bundle \
+ --keystore=/path/to/my-app.keystore \
+ --keystore-password=mykeystorepassword \
+ --key-alias=my-app-key \
+ --key-password=mykeypassword \
+ --no-tty
+```
+
+## Common Issues
+
+**Keystore-related errors:**
+- Verify the keystore file exists and the path is correct
+- Check that the keystore password is correct
+- Confirm the key alias exists in the keystore with: `keytool -list -v -keystore /path/to/keystore`
+- Verify the key password matches
+
+**Build failures:**
+- Ensure you have the latest Android SDK and build tools installed
+- Check that your `nativephp/android` directory exists and is properly initialized
+- If you've modified native code, don't use `--skip-prepare`
+
+**Play Store upload failures:**
+- Verify the Google Service Account has access to your app in Play Console
+- Ensure the service account key file is valid and readable
+- Check that your bundle ID matches your Play Console app ID
+
+## Artifact Locations
+
+Once complete, signed artifacts are located at:
+
+- APK (release): `nativephp/android/app/build/outputs/apk/release/app-release.apk`
+- AAB (bundle): `nativephp/android/app/build/outputs/bundle/release/app-release.aab`
diff --git a/resources/views/docs/mobile/4/publishing/introduction.md b/resources/views/docs/mobile/4/publishing/introduction.md
new file mode 100644
index 00000000..a1d8a753
--- /dev/null
+++ b/resources/views/docs/mobile/4/publishing/introduction.md
@@ -0,0 +1,120 @@
+---
+title: Introduction
+order: 10
+---
+
+Deploying mobile apps is a complicated process — and it's different for each platform!
+
+
+
+#### There's an Easier Way
+
+Managing certificates, provisioning profiles, keystores, and coordinating deployments across teams can be frustrating
+and time-consuming.
+
+[Bifrost](https://bifrost.nativephp.com) handles all of this complexity for you.
+
+- **Set credentials once per app** - No more managing certificates and profiles locally
+- **Team collaboration** - Share apps with your team and manage access easily
+- **Auto-deploy** - Push updates automatically without manual builds
+- **Over-the-air updates** - Deploy changes to users instantly without app store approval
+- **Low monthly cost** - Simple, affordable pricing
+
+
+
+You will be doing the following for both platforms:
+
+1. **Releasing**: Create a _release build_ for each platform.
+2. **Testing**: Test this build on real devices.
+3. **Packaging**: Sign and distribute this build to the stores.
+4. **Submitting for Review**: Go through each store's submission process to have your app reviewed.
+5. **Publishing**: Releasing the new version to the stores and your users.
+
+It's initially more time-consuming when creating a brand new app in the stores, as you need to get the listing set up
+in each store and create your signing credentials.
+
+If you've never done it before, allow a couple of hours so you can focus on getting things right and understand
+everything you need.
+
+Don't rush through the app store processes! There are compliance items that if handled incorrectly will either prevent
+you from publishing your app, being unable to release it in the territories you want to make it available to, or simply
+having it get rejected immediately when you submit it for review if you don't get those right.
+
+It's typically easier once you've released the first version of your app and after you've done 2 or 3 apps, you'll fly
+through the process!
+
+
+
+#### Need help?
+
+This page is here to help you _configure and use NativePHP_ to prepare your app for release; it is not a guide around
+the stores. You should consult the [App Store Connect Help](https://developer.apple.com/help/app-store-connect/) and
+[Play Console Help](https://support.google.com/googleplay/android-developer/?hl=en-GB#topic=3450769) documentation for
+detailed and up-to-date guidance on how to prepare your app submissions and listings.
+
+If you want more [hands-on support](/consulting), we happily work with our [Partners](/partners) to support them releasing their apps.
+
+
+
+## Releasing
+
+To prepare your app for release, bump the version number using the
+[`native:release` command](../getting-started/commands#nativerelease):
+
+```shell
+php artisan native:release patch
+```
+
+You can pass `patch`, `minor`, or `major` depending on the type of release you're cutting. This updates
+`NATIVEPHP_APP_VERSION` in your `.env` and increments the build number for you.
+
+### Versioning
+
+App version numbers should follow [semantic versioning](https://semver.org) (e.g. `1.2.3`). The `native:release` command
+relies on this format to determine how to bump your version.
+
+Remember that your app versions are usually public-facing (e.g. in store listings and on-device settings and update
+screens) and can be useful for customers to reference if they need to contact you for help and support.
+
+The app version is managed via the `NATIVEPHP_APP_VERSION` key in your `.env`.
+
+### Build numbers
+
+Both the Google Play Store and Apple App Store require your app's build number to increase for each release you submit.
+
+The build number is managed via the `NATIVEPHP_APP_VERSION_CODE` key in your `.env`. You don't need to manage this
+yourself — running `native:release` automatically increments the build number and persists it back to your `.env`.
+
+### Run a `release` build
+
+Then run a release build:
+
+```shell
+php artisan native:run --build=release
+```
+
+This builds your application with various optimizations that reduce its overall size and improve its performance, such
+as removing debugging code and unnecessary features (i.e. Composer dev dependencies).
+
+**You should test this build on a real device.** Once you're happy that everything is working as intended you can then
+submit it to the stores for approval and distribution.
+
+- [Google Play Store submission guidelines](https://support.google.com/googleplay/android-developer/answer/9859152?hl=en-GB#zippy=%2Cmaximum-size-limit)
+- [Apple App Store submission guidelines](https://developer.apple.com/ios/submit/)
+
+## Packaging Your App
+
+The `native:package` command creates signed, production-ready apps for distribution to the App Store and Play Store.
+This command handles all the complexity of code signing, building release artifacts, and preparing files for submission.
+
+Packaging is platform-specific — follow the [Android](android) or [iOS](ios) guide for step-by-step instructions.
+
+## Before You Begin
+
+Before you can package your app for distribution, ensure:
+
+1. Your app is fully developed and tested on both platforms
+2. You have a valid bundle ID and app ID configured in your `nativephp.php` config
+3. For Android: You have a signing keystore with a valid key alias
+4. For iOS: You have the necessary signing certificates and provisioning profiles from Apple Developer
+5. All configuration is complete (see the [configuration guide](../getting-started/configuration))
diff --git a/resources/views/docs/mobile/4/publishing/ios.md b/resources/views/docs/mobile/4/publishing/ios.md
new file mode 100644
index 00000000..5d258e6b
--- /dev/null
+++ b/resources/views/docs/mobile/4/publishing/ios.md
@@ -0,0 +1,193 @@
+---
+title: iOS
+order: 30
+---
+
+Package and sign your app for the Apple App Store. Read the [Introduction](introduction) first, and make sure
+you've cut a [release build](introduction#releasing).
+
+## Required Signing Credentials
+
+iOS apps require several credentials from Apple Developer:
+
+| Credential | Option | Environment Variable | Description |
+|-----------|--------|----------------------|-------------|
+| API Key file | `--api-key-path` | `APP_STORE_API_KEY_PATH` | Path to `.p8` file from App Store Connect |
+| API Key ID | `--api-key-id` | `APP_STORE_API_KEY_ID` | Key ID from App Store Connect |
+| API Issuer ID | `--api-issuer-id` | `APP_STORE_API_ISSUER_ID` | Issuer ID from App Store Connect |
+| Certificate | `--certificate-path` | `IOS_DISTRIBUTION_CERTIFICATE_PATH` | Distribution certificate (`.p12` or `.cer`) |
+| Certificate password | `--certificate-password` | `IOS_DISTRIBUTION_CERTIFICATE_PASSWORD` | Password for the certificate |
+| Provisioning profile | `--provisioning-profile-path` | `IOS_DISTRIBUTION_PROVISIONING_PROFILE_PATH` | Profile file (`.mobileprovision`) |
+| Team ID | `--team-id` | `IOS_TEAM_ID` | Apple Developer Team ID |
+
+## Setting Up App Store Connect API
+
+To upload to the App Store directly from the command line, you'll need an API key:
+
+1. Log in to [App Store Connect](https://appstoreconnect.apple.com)
+2. Navigate to Users & Access → Keys
+3. Click the "+" button to create a new key with "Developer" access
+4. Download the `.p8` file immediately (you can't download it again later)
+5. Note the Key ID and Issuer ID displayed on the page
+
+## Export Methods
+
+The `--export-method` option controls how your app is packaged:
+
+- `app-store` - For App Store distribution (default)
+- `ad-hoc` - For distribution to specific registered devices
+- `enterprise` - For enterprise distribution (requires enterprise program)
+- `development` - For development and testing
+
+## Building for App Store
+
+To build a production app ready for App Store submission:
+
+```bash
+php artisan native:package ios \
+ --export-method=app-store \
+ --api-key-path=/path/to/api-key.p8 \
+ --api-key-id=ABC123DEF \
+ --api-issuer-id=01234567-89ab-cdef-0123-456789abcdef \
+ --certificate-path=/path/to/distribution.p12 \
+ --certificate-password=certificatepassword \
+ --provisioning-profile-path=/path/to/profile.mobileprovision \
+ --team-id=ABC1234567
+```
+
+## Building for Ad-Hoc Distribution
+
+For distributing to specific devices without going through the App Store:
+
+```bash
+php artisan native:package ios \
+ --export-method=ad-hoc \
+ --certificate-path=/path/to/distribution.p12 \
+ --certificate-password=certificatepassword \
+ --provisioning-profile-path=/path/to/ad-hoc-profile.mobileprovision
+```
+
+## Building for Development
+
+For testing on your own device:
+
+```bash
+php artisan native:package ios \
+ --export-method=development \
+ --certificate-path=/path/to/development.p12 \
+ --certificate-password=certificatepassword \
+ --provisioning-profile-path=/path/to/development-profile.mobileprovision
+```
+
+## Using Environment Variables
+
+Store your iOS credentials in `.env`:
+
+```env
+APP_STORE_API_KEY_PATH=/path/to/api-key.p8
+APP_STORE_API_KEY_ID=ABC123DEF
+APP_STORE_API_ISSUER_ID=01234567-89ab-cdef-0123-456789abcdef
+IOS_DISTRIBUTION_CERTIFICATE_PATH=/path/to/distribution.p12
+IOS_DISTRIBUTION_CERTIFICATE_PASSWORD=certificatepassword
+IOS_DISTRIBUTION_PROVISIONING_PROFILE_PATH=/path/to/profile.mobileprovision
+IOS_TEAM_ID=ABC1234567
+```
+
+Then build with:
+
+```bash
+php artisan native:package ios --export-method=app-store
+```
+
+## Uploading to App Store Connect
+
+To automatically upload after building:
+
+```bash
+php artisan native:package ios \
+ --export-method=app-store \
+ --api-key-path=/path/to/api-key.p8 \
+ --api-key-id=ABC123DEF \
+ --api-issuer-id=01234567-89ab-cdef-0123-456789abcdef \
+ --certificate-path=/path/to/distribution.p12 \
+ --certificate-password=certificatepassword \
+ --provisioning-profile-path=/path/to/profile.mobileprovision \
+ --team-id=ABC1234567 \
+ --upload-to-app-store
+```
+
+The upload uses the App Store Connect API, so API credentials are required only when using `--upload-to-app-store`.
+
+## Validating Provisioning Profiles
+
+Before building, you can validate your provisioning profile to check push notification support and entitlements:
+
+```bash
+php artisan native:package ios \
+ --validate-profile \
+ --provisioning-profile-path=/path/to/profile.mobileprovision
+```
+
+This extracts and displays:
+
+- Profile name
+- All entitlements configured in the profile
+- Push notification support status
+- Associated domains
+- APS environment matching
+
+## Testing App Store Uploads
+
+To test uploading an existing IPA without rebuilding:
+
+```bash
+php artisan native:package ios \
+ --test-upload \
+ --api-key-path=/path/to/api-key.p8 \
+ --api-key-id=ABC123DEF \
+ --api-issuer-id=01234567-89ab-cdef-0123-456789abcdef
+```
+
+## Clearing Xcode Caches
+
+If you encounter build issues, clear Xcode and Swift Package Manager caches:
+
+```bash
+php artisan native:package ios \
+ --export-method=app-store \
+ --clean-caches
+```
+
+## Forcing a Clean Rebuild
+
+To force a complete rebuild and create a new archive:
+
+```bash
+php artisan native:package ios \
+ --export-method=app-store \
+ --rebuild
+```
+
+## Validating Without Exporting
+
+To validate the archive without creating an IPA:
+
+```bash
+php artisan native:package ios \
+ --validate-only
+```
+
+## Version Numbers
+
+`native:release` is a convenient way to bump your app's version number — pass `major`, `minor`, or `patch`:
+
+```bash
+php artisan native:release patch
+```
+
+Build numbers (`NATIVEPHP_APP_VERSION_CODE`) are incremented automatically based on your `.env`, so you don't have to
+manage them by hand.
+
+## Artifact Locations
+
+Once complete, your signed IPA is generated in Xcode's build output directory.
diff --git a/resources/views/docs/mobile/4/super-native/_index.md b/resources/views/docs/mobile/4/super-native/_index.md
deleted file mode 100644
index 4408d8b9..00000000
--- a/resources/views/docs/mobile/4/super-native/_index.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: SuperNative
-order: 45
----
diff --git a/resources/views/docs/mobile/4/super-native/events.md b/resources/views/docs/mobile/4/super-native/events.md
deleted file mode 100644
index 125d98b2..00000000
--- a/resources/views/docs/mobile/4/super-native/events.md
+++ /dev/null
@@ -1,68 +0,0 @@
----
-title: Events
-order: 40
----
-
-## Overview
-
-Screens react to things that happen outside a user tap — a push notification arrives, a websocket message lands,
-a bridge call finishes. A component **listens** for these native events and updates its state in response; the
-screen re-renders like any other state change.
-
-This page covers listening from a component. For the async bridge-response side (calling a native API and handling
-its result or a custom event class), see [Events](../the-basics/events).
-
-## Listening with #[On]
-
-Annotate a method with `#[On(EventClass::class)]` and it runs whenever that event fires. The method's parameters
-are bound **by name** from the event's public properties:
-
-```php
-use Native\Mobile\Attributes\On;
-use NativePHP\Vibe\Events\MessageReceived;
-
-class ChatScreen extends NativeComponent
-{
- public array $messages = [];
-
- #[On(MessageReceived::class)]
- public function onMessage(string $body, string $from): void
- {
- $this->messages[] = ['body' => $body, 'from' => $from];
- }
-}
-```
-
-`#[On]` is repeatable — stack several on one method to handle multiple events, or put several methods on the same
-event. Listeners are torn down automatically when the screen unmounts, so they never leak onto the next screen.
-
-## Listening with ->on()
-
-For a listener you register at runtime — for example inside `mount()`, or conditionally — use the fluent `->on()`
-method with a closure:
-
-```php
-public function mount(): void
-{
- $this->on(OrderShipped::class, function ($event) {
- $this->status = "Shipped: {$event->trackingNumber}";
- });
-}
-```
-
-Use `#[On]` for the common case (a fixed listener declared on the class) and `->on()` when you need to wire one up
-dynamically.
-
-## Where events come from
-
-Native events originate on the device side and are delivered to whichever screen is alive: plugin events (a
-[Vibe](../plugins/vibe) websocket message, a push notification tap), bridge-call completions, and any custom
-events an async native call resolves with. Because delivery targets the live screen, a listener only fires while
-its screen is on the stack.
-
-
-
-You can drive events in tests without a device — `emitNative(Event::class, [...])` delivers one straight to the
-component. See [Native Events & the Bridge](../testing/native-events).
-
-
diff --git a/resources/views/docs/mobile/4/super-native/introduction.md b/resources/views/docs/mobile/4/super-native/introduction.md
deleted file mode 100644
index 6982762e..00000000
--- a/resources/views/docs/mobile/4/super-native/introduction.md
+++ /dev/null
@@ -1,102 +0,0 @@
----
-title: Introduction
-order: 1
----
-
-## What is SuperNative?
-
-SuperNative is the headline feature of NativePHP for Mobile v4: it drops the web view entirely in favor of fully
-native UI — SwiftUI on iOS and Jetpack Compose on Android.
-
-Your screens are no longer HTML rendered inside a browser shell. They are real, platform-native views, built and
-updated by your PHP code. Same Laravel app, same Blade templates — genuinely native UI.
-
-SuperNative is **the default** in v4. New apps render native screens from the very first route — no configuration
-required. Prefer to keep building with the web view? [Opting out](#is-the-web-view-still-an-option) is one route
-and one component.
-
-## Try it now
-
-The fastest way to see SuperNative is to run the demo app,
-[`nativephp/super-native`](https://github.com/nativephp/super-native), on a simulator or device.
-
-You'll need a working NativePHP for Mobile [development environment](../getting-started/environment-setup) first
-(Xcode for iOS, Android Studio for Android). Then clone the demo, install it, and run it:
-
-```shell
-git clone https://github.com/nativephp/super-native
-cd super-native
-composer install
-php artisan native:install
-php artisan native:run
-```
-
-`native:run` builds the app and launches it on your connected device or simulator. Explore the source to see how
-the screens are built, then start swapping in your own.
-
-## How it works
-
-SuperNative builds on three ideas working together:
-
-- **Shared memory with PHP** — the native layer and your PHP application share memory directly, so there's no
- network round-trip, no serialization overhead, and no waiting on a web view bridge. State changes flow between
- PHP and the native UI almost instantly.
-- **Livewire-like components** — each screen is driven by a PHP component class that holds its state and behavior,
- just like a Livewire component. User interactions call your methods, your properties update, and the UI
- re-renders to match.
-- **Blade components for DX** — you define your UI with the same [EDGE component](../edge-components/introduction)
- syntax you already know. Familiar, expressive Blade templates compile down to native SwiftUI and Compose views.
-
-If you've built anything with Livewire, you already know how to build with SuperNative.
-
-For a look under the hood — how Blade becomes SwiftUI and Compose, how state flows across the shared-memory
-boundary, and the threading model behind it — see the [Architecture](../architecture/overview) section.
-
-## Why SuperNative?
-
-Two reasons above all:
-
-- **Performance** — native views render and animate at full platform speed. No web view startup cost, no DOM, no
- JavaScript bridge. Scrolling, transitions and gestures feel exactly the way users expect because they're powered
- by the same UI frameworks every other native app uses.
-- **Accessibility** — SwiftUI and Jetpack Compose come with the platform's accessibility support built in.
- Screen readers, dynamic type, contrast settings and assistive controls work with your app out of the box,
- rather than being approximated through a browser.
-
-## Is the web view still an option?
-
-Yes. SuperNative applies **per route**: a URL you register with `Route::native()` renders as a native screen,
-while every other route renders in the web view as ordinary Laravel HTML — exactly as it did in v3.
-
-```php
-// Native screen — SuperNative renders it natively
-Route::native('/dashboard', Dashboard::class);
-
-// A normal route — served to the web view as HTML, unchanged
-Route::get('/settings', fn () => view('settings'));
-```
-
-So an existing app keeps working web-view-first with no changes, and you adopt SuperNative one screen at a time
-whenever you're ready — or not at all.
-
-## What's coming
-
-SuperNative is in beta and moving quickly. On the roadmap:
-
-- [Jump](../the-basics/jump) support
-- A revised kitchen sink app
-- Full plugin support for all first-party plugins
-
-## For Plugin Developers
-
-There are **no breaking changes** in our plugin architecture for v4.
-
-Start updating your plugins now by widening the `nativephp/mobile` constraint in your plugin's `composer.json` to
-allow v4:
-
-```json
-"require": {
- "nativephp/mobile": "^3.0" // [tl! remove]
- "nativephp/mobile": "^3.0|^4.0" // [tl! add]
-}
-```
diff --git a/resources/views/docs/mobile/4/testing/accessibility.md b/resources/views/docs/mobile/4/testing/accessibility.md
index 20fdf903..ae396391 100644
--- a/resources/views/docs/mobile/4/testing/accessibility.md
+++ b/resources/views/docs/mobile/4/testing/accessibility.md
@@ -10,7 +10,7 @@ test that drives its behavior. `assertAccessible()` walks the rendered wire tree
reader cares about and fails with a located list of every violation, so an unlabeled control can't ship unnoticed.
This checks that the tree carries the right labels — it does not replace a manual VoiceOver/TalkBack pass. See
-[Accessibility](../super-native/accessibility) for the labeling rules the audit enforces and the platform
+[Accessibility](../digging-deeper/accessibility) for the labeling rules the audit enforces and the platform
behavior you get for free.
## assertAccessible()
diff --git a/resources/views/docs/mobile/4/testing/introduction.md b/resources/views/docs/mobile/4/testing/introduction.md
index 60db584f..8dda34fe 100644
--- a/resources/views/docs/mobile/4/testing/introduction.md
+++ b/resources/views/docs/mobile/4/testing/introduction.md
@@ -5,7 +5,7 @@ order: 100
## Overview
-NativePHP ships a testing suite for your [SuperNative](../super-native/introduction) screens. It mounts a
+NativePHP ships a testing suite for your [SuperNative](../architecture/super-native) screens. It mounts a
`NativeComponent`, renders its Blade, dispatches events, and re-renders — the whole component lifecycle — entirely
in-process.
diff --git a/resources/views/docs/mobile/4/super-native/device.md b/resources/views/docs/mobile/4/the-basics/device.md
similarity index 96%
rename from resources/views/docs/mobile/4/super-native/device.md
rename to resources/views/docs/mobile/4/the-basics/device.md
index b196bad9..53f436cc 100644
--- a/resources/views/docs/mobile/4/super-native/device.md
+++ b/resources/views/docs/mobile/4/the-basics/device.md
@@ -1,6 +1,6 @@
---
title: Device
-order: 100
+order: 110
---
## Overview
@@ -8,7 +8,7 @@ order: 100
The `Device` API exposes device hardware and information — vibration, the flashlight, a stable device
identifier, and device + battery details.
-It's a **core built-in** in v4: the facade resolves with nothing to install or register.
+It's a **core built-in**: the facade resolves with nothing to install or register.
```php
use Native\Mobile\Facades\Device;
diff --git a/resources/views/docs/mobile/4/super-native/dialog.md b/resources/views/docs/mobile/4/the-basics/dialogs.md
similarity index 96%
rename from resources/views/docs/mobile/4/super-native/dialog.md
rename to resources/views/docs/mobile/4/the-basics/dialogs.md
index 64680c31..ded552db 100644
--- a/resources/views/docs/mobile/4/super-native/dialog.md
+++ b/resources/views/docs/mobile/4/the-basics/dialogs.md
@@ -1,6 +1,6 @@
---
-title: Dialog
-order: 110
+title: Dialogs
+order: 160
---
## Overview
@@ -8,7 +8,7 @@ order: 110
The `Dialog` API shows native alert dialogs and toast/snackbar notifications — a real `UIAlertController` on
iOS and `AlertDialog` on Android, so they look and feel exactly like the platform.
-It's a **core built-in** in v4: the facade resolves with nothing to install or register.
+It's a **core built-in**: the facade resolves with nothing to install or register.
```php
use Native\Mobile\Facades\Dialog;
diff --git a/resources/views/docs/mobile/4/the-basics/events.md b/resources/views/docs/mobile/4/the-basics/events.md
index dcae60fd..dd2ca72f 100644
--- a/resources/views/docs/mobile/4/the-basics/events.md
+++ b/resources/views/docs/mobile/4/the-basics/events.md
@@ -5,211 +5,63 @@ order: 200
## Overview
-Many native mobile operations take time to complete and await user interaction. PHP isn't really set up to handle this
-sort of asynchronous behaviour; it is built to do its work, send a response and move on as quickly as possible.
+Screens react to things that happen outside a user tap — a push notification arrives, a websocket message lands,
+a bridge call finishes. A component **listens** for these native events and updates its state in response; the
+screen re-renders like any other state change.
-NativePHP for Mobile smooths over this disparity between the different paradigms using a simple event system that
-handles completion of asynchronous methods using a webhook-/websocket-style approach to notify your Laravel app.
+This page covers listening for these native events from a component.
-## Understanding Async vs Sync
+## Listening with #[On]
-Not all actions are async. Some methods run immediately, and in some cases return a result straight away.
-
-Here are a few of the **synchronous** APIs:
-
-```php
-Haptics::vibrate();
-System::flashlight();
-Dialog::toast('Hello!');
-```
-Asynchronous actions trigger operations that may complete later. These return immediately, usually with a `bool` or
-`void`, allowing PHP's execution to finish. In many of these cases, the user interacts directly with a native component.
-
-When the user has completed their task and the native UI is dismissed, the app will emit an event that represents the
-outcome.
-
-The _type_ (the class name) of the event and its properties all help you to choose the appropriate action to take in
-response to the outcome.
-
-```php
-// These trigger operations and fire events when complete
-Camera::getPhoto(); // → PhotoTaken event
-Biometrics::prompt(); // → Completed event
-PushNotifications::enroll(); // → TokenGenerated event
-```
-
-## Basic Event Structure
-
-All events are standard [Laravel Event classes](https://laravel.com/docs/12.x/events#defining-events). The public
-properties of the events contain the pertinent data coming from the native app side.
-
-## Custom Events
-
-Almost every function that emits events can be customized to emit events that you define. This is a great way to ensure
-only the relevant listeners are executed when these events are fired.
-
-Events are simple PHP classes that receive some parameters. You can extend existing events for convenience.
-
-Let's see a complete example...
-
-### Define your custom event class
+Annotate a method with `#[On(EventClass::class)]` and it runs whenever that event fires. The method's parameters
+are bound **by name** from the event's public properties:
```php
-namespace App\Events;
+use Native\Mobile\Attributes\On;
+use NativePHP\Vibe\Events\MessageReceived;
-use Native\Mobile\Events\Alert\ButtonPressed;
+class ChatScreen extends NativeComponent
+{
+ public array $messages = [];
-class MyButtonPressedEvent extends ButtonPressed
-{}
+ #[On(MessageReceived::class)]
+ public function onMessage(string $body, string $from): void
+ {
+ $this->messages[] = ['body' => $body, 'from' => $from];
+ }
+}
```
-### Pass this class to an async function
+`#[On]` is repeatable — stack several on one method to handle multiple events, or put several methods on the same
+event. Listeners are torn down automatically when the screen unmounts, so they never leak onto the next screen.
-```php
-use App\Events\MyButtonPressedEvent;
-
-Dialog::alert('Warning!', 'You are about to delete everything! Are you sure?', [
- 'Cancel',
- 'Do it!'
- ])
- ->event(MyButtonPressedEvent::class)
-```
+## Listening with ->on()
-### Handle the event
-
-Here's an example handling a custom event class inside a component.
+For a listener you register at runtime — for example inside `mount()`, or conditionally — use the fluent `->on()`
+method with a closure:
```php
-use App\Events\MyButtonPressed;
-use Native\Mobile\Attributes\OnNative;
-
-#[OnNative(MyButtonPressed::class)]
-public function buttonPressed()
+public function mount(): void
{
- // Do stuff
+ $this->on(OrderShipped::class, function ($event) {
+ $this->status = "Shipped: {$event->trackingNumber}";
+ });
}
```
-## Event Handling
-
-All asynchronous methods follow the same pattern:
-
-1. **Call the method** to trigger the operation.
-2. **Listen for the appropriate events** to handle the result.
-3. **Update your UI** based on the outcome.
-
-All events get sent directly to JavaScript in the web view _and_ to your PHP application via a special route. This
-allows you to listen for these events in the context that best suits your application.
-
-### On the frontend
-
-Events are 'broadcast' to the frontend of your application via the web view through a custom `Native` helper. You can
-easily listen for these events through JavaScript in a few ways:
-
-- The globally available `Native.on()` helper
-- Directly importing the `On` function
-- The `#[OnNative()]` PHP attribute Livewire extension
-
-
-
-Typically, you shouldn't need to use more than one of these approaches. Which one you adopt will depend on which
-frontend stack you're using to build your app.
-
-
-
-#### The `Native.on()` helper
-
-Register the event listener directly in JavaScript:
-
-```blade
-@@use(Native\Mobile\Events\Alert\ButtonPressed)
-
-
-```
+Use `#[On]` for the common case (a fixed listener declared on the class) and `->on()` when you need to wire one up
+dynamically.
-This approach is useful if you're not using any particular frontend JavaScript framework.
+## Where events come from
-#### The `On` import
+Native events originate on the device side and are delivered to whichever screen is alive: plugin events (a
+[Vibe](../digging-deeper/websockets) websocket message, a push notification tap), bridge-call completions, and any custom
+events an async native call resolves with. Because delivery targets the live screen, a listener only fires while
+its screen is on the stack.
-Make sure you've [set up the `Native` plugin](native-functions#install-the-plugin) in your `package.json` first.
+You can drive events in tests without a device — `emitNative(Event::class, [...])` delivers one straight to the
+component. See [Native Events & the Bridge](../testing/native-events).
-
-If you're using a SPA framework like Vue or React, it's more convenient to import the `On` function directly to
-register your event listeners. Here's an example using the amazing Vue:
-
-```js
-import { On, Events } from '#nativephp';
-import { onMounted } from 'vue';
-
-const handleButtonPressed = (payload: any) => {};
-
-onMounted(() => {
- On(Events.Alert.ButtonPressed, handleButtonPressed);
-});
-```
-
-Note how we're also using the `Events` object above to simplify our use of built-in event names. For custom event
-classes, you will need to reference these by their full name:
-
-```js
-On('App\\Events\\MyButtonPressedEvent', handleButtonPressed);
-```
-
-In SPA land, don't forget to de-register your event handlers using the `Off` function too:
-
-```js
-import { Off, Events } from '#nativephp';
-import { onUnmounted } from 'vue';
-
-onUnmounted(() => {
- Off(Events.Alert.ButtonPressed, handleButtonPressed);
-});
-```
-
-#### The `#[OnNative()]` attribute
-
-Livewire makes listening to 'broadcast' events simple. Just add the `#[OnNative()]` attribute attached to the Livewire
-component method you want to use as its handler:
-
-```php
-use Native\Mobile\Attributes\OnNative;
-use Native\Mobile\Events\Camera\PhotoTaken;
-
-#[OnNative(PhotoTaken::class)]
-public function handlePhoto(string $path)
-{
- // Handle captured photo
-}
-```
-
-### On the backend
-
-You can also listen for these events on the PHP side as they are simultaneously passed to your Laravel application.
-
-Simply [add a listener](https://laravel.com/docs/12.x/events#registering-events-and-listeners) as you normally would:
-
-```php
-use App\Services\APIService;
-use Native\Mobile\Events\Camera\PhotoTaken;
-
-class UpdateAvatar
-{
- public function __construct(private APIService $api) {}
-
- public function handle(PhotoTaken $event): void
- {
- $imageData = base64_encode(
- file_get_contents($event->path)
- );
-
- $this->api->updateAvatar($imageData);
- }
-}
-```
diff --git a/resources/views/docs/mobile/4/super-native/file.md b/resources/views/docs/mobile/4/the-basics/file.md
similarity index 92%
rename from resources/views/docs/mobile/4/super-native/file.md
rename to resources/views/docs/mobile/4/the-basics/file.md
index 0820be46..dc15dbc4 100644
--- a/resources/views/docs/mobile/4/super-native/file.md
+++ b/resources/views/docs/mobile/4/the-basics/file.md
@@ -1,6 +1,6 @@
---
title: File
-order: 120
+order: 130
---
## Overview
@@ -8,7 +8,7 @@ order: 120
The `File` API performs native file operations — moving and copying files — using each platform's file system
directly.
-It's a **core built-in** in v4: the facade resolves with nothing to install or register.
+It's a **core built-in**: the facade resolves with nothing to install or register.
```php
use Native\Mobile\Facades\File;
diff --git a/resources/views/docs/mobile/4/the-basics/jump.md b/resources/views/docs/mobile/4/the-basics/jump.md
index 103ccad2..a067acdf 100644
--- a/resources/views/docs/mobile/4/the-basics/jump.md
+++ b/resources/views/docs/mobile/4/the-basics/jump.md
@@ -27,7 +27,7 @@ device, executed natively, and the result is sent back to PHP — exactly as it
1. Install Jump on your device from the
[App Store](https://apps.apple.com/us/app/bifrost-jump/id6757173334) or
[Google Play](https://play.google.com/store/apps/details?id=com.bifrosttech.jump). You'll need **Jump v2 or
- later** to work with NativePHP Mobile v3.3.
+ later**.
2. Make sure your phone and your computer are on the same Wi-Fi network.
3. From your Laravel project, run:
@@ -44,7 +44,7 @@ reload automatically.
#### New to NativePHP?
-If you're just starting out, the [Quick Start](/docs/mobile/3/getting-started/quick-start) walks you through installing
+If you're just starting out, the [Quick Start](../getting-started/quick-start) walks you through installing
NativePHP and running Jump for the first time.
@@ -110,10 +110,10 @@ The dev server is configured under the `server` key in your `config/nativephp.ph
],
```
-See [Configuration](/docs/mobile/3/getting-started/configuration#development-server) for the full reference.
+See [Configuration](../getting-started/configuration#development-server) for the full reference.
Per-run options (ports, host, IP, mDNS) can all be overridden on the command line — see the
-[`native:jump` command reference](/docs/mobile/3/getting-started/commands#nativejump) for the full list of flags.
+[`native:jump` command reference](../getting-started/commands#nativejump) for the full list of flags.
### Multiple network interfaces
@@ -173,4 +173,4 @@ the managed `artisan serve` process cleanly.
| You want to share a preview with a teammate over the same network | You need to test app start-up behaviour or bundled assets |
For most day-to-day development, Jump is the fastest way to see changes on a real device. Once you're ready to ship,
-use [`native:package`](/docs/mobile/3/getting-started/commands#nativepackage) to build a signed binary.
+use [`native:package`](../getting-started/commands#nativepackage) to build a signed binary.
diff --git a/resources/views/docs/mobile/4/super-native/layouts.md b/resources/views/docs/mobile/4/the-basics/layouts.md
similarity index 99%
rename from resources/views/docs/mobile/4/super-native/layouts.md
rename to resources/views/docs/mobile/4/the-basics/layouts.md
index 9724f93b..0f270a83 100644
--- a/resources/views/docs/mobile/4/super-native/layouts.md
+++ b/resources/views/docs/mobile/4/the-basics/layouts.md
@@ -1,6 +1,6 @@
---
title: Layouts
-order: 60
+order: 154
---
## Overview
@@ -108,7 +108,7 @@ in a screen's Blade.
- `elevation(int $px)` — hairline thickness at the bottom of the bar
- `displayMode(string)` — `large`, `inline`, or `automatic`
- `scrollBehavior(string)` — `collapse`, `pinned`, or `enterAlways`
-- `searchBar(string $placeholder = '', ?string $onQuery = null, int $debounceMs = 300)` — attach a native search bar (see [Search](search))
+- `searchBar(string $placeholder = '', ?string $onQuery = null, int $debounceMs = 300)` — attach a native search bar (see [Search](../digging-deeper/search))
- `action(NavAction $action)` — append a trailing action
### `NavAction` — a top-bar button or menu
diff --git a/resources/views/docs/mobile/4/the-basics/native-ui.md b/resources/views/docs/mobile/4/the-basics/native-ui.md
index 93c9b470..3106959a 100644
--- a/resources/views/docs/mobile/4/the-basics/native-ui.md
+++ b/resources/views/docs/mobile/4/the-basics/native-ui.md
@@ -9,7 +9,7 @@ Your app's UI is fully native — SwiftUI on iOS, Jetpack Compose on Android —
Two pieces work together to make that happen:
-- **[SuperNative](../super-native/introduction)** is the engine. Each screen is a PHP component class — think
+- **[SuperNative](../architecture/super-native)** is the engine. Each screen is a PHP component class — think
Livewire, but for native views. It holds your screen's state and behavior, shares memory directly with the native
side, and re-renders the UI whenever your properties change.
- **[EDGE](../edge-components/introduction)** (Element Definition and Generation Engine) is the component language.
@@ -42,15 +42,15 @@ Browse the full catalogue in the [EDGE Components](../edge-components/introducti
Once you're comfortable with the component syntax, two concepts organize your screens into an app:
-### Navigation
+### Routing
-[Navigation](../super-native/navigation) works like a native navigation stack, because it is one. Register screens with
+[Routing](../the-basics/routing) works like a native navigation stack, because it is one. Register screens with
`Route::native()`, then push, pop, and replace them from inside your components — with native transitions, route
parameters, and data passing along the way.
### Layouts
-[Layouts](../super-native/layouts) wrap the screens routed beneath them with shared chrome — a top nav bar, a bottom tab bar, or
+[Layouts](../the-basics/layouts) wrap the screens routed beneath them with shared chrome — a top nav bar, a bottom tab bar, or
both — so individual screens stay focused on their content. Declare the chrome once in a `NativeLayout` class,
attach it to a route or group of routes, and the framework swaps between "tabs" and "stack" chrome automatically as
users move around.
diff --git a/resources/views/docs/mobile/4/super-native/navigation.md b/resources/views/docs/mobile/4/the-basics/routing.md
similarity index 99%
rename from resources/views/docs/mobile/4/super-native/navigation.md
rename to resources/views/docs/mobile/4/the-basics/routing.md
index dd63fb33..126f0969 100644
--- a/resources/views/docs/mobile/4/super-native/navigation.md
+++ b/resources/views/docs/mobile/4/the-basics/routing.md
@@ -1,6 +1,6 @@
---
-title: Navigation
-order: 50
+title: Routing
+order: 152
---
## Overview
diff --git a/resources/views/docs/mobile/4/the-basics/safe-area.md b/resources/views/docs/mobile/4/the-basics/safe-area.md
index 6d4d4ec2..99153fe9 100644
--- a/resources/views/docs/mobile/4/the-basics/safe-area.md
+++ b/resources/views/docs/mobile/4/the-basics/safe-area.md
@@ -50,7 +50,7 @@ Column::make()->safeAreaBottom(); // bottom only
## Layouts already handle this for you
-When a screen is wrapped by a [layout](../super-native/layouts), the framework's `wrapWithChrome` flow picks the right safe-area
+When a screen is wrapped by a [layout](../the-basics/layouts), the framework's `wrapWithChrome` flow picks the right safe-area
variant for the wrapper based on which chrome is present:
| Chrome | Wrapper inset | Why |
diff --git a/resources/views/docs/mobile/4/super-native/system.md b/resources/views/docs/mobile/4/the-basics/system.md
similarity index 88%
rename from resources/views/docs/mobile/4/super-native/system.md
rename to resources/views/docs/mobile/4/the-basics/system.md
index a3a12c29..7a6011f9 100644
--- a/resources/views/docs/mobile/4/super-native/system.md
+++ b/resources/views/docs/mobile/4/the-basics/system.md
@@ -1,13 +1,13 @@
---
title: System
-order: 130
+order: 140
---
## Overview
The `System` API covers system-level concerns — platform detection and opening the app's settings screen.
-It's a **core built-in** in v4: the facade resolves with nothing to install or register.
+It's a **core built-in**: the facade resolves with nothing to install or register.
```php
use Native\Mobile\Facades\System;
@@ -68,7 +68,7 @@ System::appSettings();
## Appearance (light / dark)
Reading the current appearance and reacting to theme changes lives with the rest of theming — see
-[Theming → Appearance in PHP](theming#appearance-in-php) for `System::appearance()`, `isDarkMode()`,
+[Theming → Appearance in PHP](../digging-deeper/theming#appearance-in-php) for `System::appearance()`, `isDarkMode()`,
`isLightMode()`, the `isDark()` / `theme()` helpers, and the `AppearanceChanged` event.
diff --git a/routes/web.php b/routes/web.php
index c825879b..7d688d16 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -73,11 +73,14 @@
Route::redirect('tshirt', 'blog/nativephp-for-mobile-is-now-free');
// v4: Device/Dialog/File/System moved from plugins into core built-ins; their docs
-// now live in the SuperNative section (must precede the generic core-plugin redirect below).
-foreach (['device', 'dialog', 'file', 'system'] as $corePage) {
- Route::redirect("docs/mobile/4/plugins/core/{$corePage}", "/docs/mobile/4/super-native/{$corePage}", 301);
+// now live in The Basics (must precede the generic core-plugin redirect below).
+foreach (['device', 'file', 'system'] as $corePage) {
+ Route::redirect("docs/mobile/4/plugins/core/{$corePage}", "/docs/mobile/4/the-basics/{$corePage}", 301);
}
+// The Dialog docs live at the "dialogs" slug in The Basics.
+Route::redirect('docs/mobile/4/plugins/core/dialog', '/docs/mobile/4/the-basics/dialogs', 301);
+
// Redirect mobile core plugin docs to plugin directory pages
Route::get('docs/mobile/{version}/plugins/core/{page}', function (string $version, string $page) {
return redirect("/plugins/nativephp/mobile-{$page}", 301);
@@ -88,9 +91,15 @@
return redirect("/plugins/nativephp/mobile-{$page}", 301);
})->where('version', '[0-9]+')->where('page', '[a-z-]+');
-// v4: navigation & layouts moved from The Basics into the SuperNative section
-Route::redirect('docs/mobile/4/the-basics/navigation', '/docs/mobile/4/super-native/navigation', 301);
-Route::redirect('docs/mobile/4/the-basics/layouts', '/docs/mobile/4/super-native/layouts', 301);
+// v4: the SuperNative section was flattened; its old section root now points at
+// the SuperNative overview page, which lives in the Architecture section.
+Route::redirect('docs/mobile/4/super-native', '/docs/mobile/4/architecture/super-native', 301);
+
+// v4: the Architecture Overview page was removed; the SuperNative page now leads the section.
+Route::redirect('docs/mobile/4/architecture/overview', '/docs/mobile/4/architecture/super-native', 301);
+
+// v4: the old the-basics/navigation slug is now the-basics/routing (Layouts lives at the-basics/layouts directly).
+Route::redirect('docs/mobile/4/the-basics/navigation', '/docs/mobile/4/the-basics/routing', 301);
// v4: Screen & Card components were removed — theme surfaces with bg-theme-* classes instead
Route::redirect('docs/mobile/4/edge-components/screen', '/docs/mobile/4/edge-components/layout', 301);
diff --git a/tests/Feature/DocsPrereleaseVersionTest.php b/tests/Feature/DocsPrereleaseVersionTest.php
index ca4cd532..de211842 100644
--- a/tests/Feature/DocsPrereleaseVersionTest.php
+++ b/tests/Feature/DocsPrereleaseVersionTest.php
@@ -123,4 +123,115 @@ public function test_version_switcher_maps_renamed_pages_between_versions(): voi
$service->resolvePageForVersion('mobile', 3, 'the-basics/overview'),
);
}
+
+ public function test_concepts_pages_redirect_to_digging_deeper_within_v4(): void
+ {
+ $this->get('/docs/mobile/4/concepts/security')
+ ->assertRedirect('/docs/mobile/4/digging-deeper/security');
+ }
+
+ public function test_old_super_native_urls_redirect_to_their_new_homes(): void
+ {
+ // The old SuperNative section root forwards to the overview page in Architecture.
+ $this->get('/docs/mobile/4/super-native')
+ ->assertRedirect('/docs/mobile/4/architecture/super-native');
+
+ // The old SuperNative introduction now forwards to the Architecture page.
+ $this->get('/docs/mobile/4/super-native/introduction')
+ ->assertRedirect('/docs/mobile/4/architecture/super-native');
+
+ // Navigation and Layouts moved into The Basics (Navigation renamed to Routing).
+ $this->get('/docs/mobile/4/super-native/navigation')
+ ->assertRedirect('/docs/mobile/4/the-basics/routing');
+ $this->get('/docs/mobile/4/super-native/layouts')
+ ->assertRedirect('/docs/mobile/4/the-basics/layouts');
+
+ // Other former sub-pages forward to direct Digging Deeper pages.
+ $this->get('/docs/mobile/4/super-native/data-binding')
+ ->assertRedirect('/docs/mobile/4/digging-deeper/data-binding');
+ }
+
+ public function test_removed_architecture_overview_redirects_to_super_native(): void
+ {
+ $this->get('/docs/mobile/4/architecture/overview')
+ ->assertRedirect('/docs/mobile/4/architecture/super-native');
+ }
+
+ public function test_deployment_page_moved_into_the_publishing_section(): void
+ {
+ // Old getting-started/deployment URL forwards to the new Publishing intro.
+ $this->get('/docs/mobile/4/getting-started/deployment')
+ ->assertRedirect('/docs/mobile/4/publishing/introduction');
+
+ // The three split pages render.
+ $this->get('/docs/mobile/4/publishing/introduction')->assertOk()->assertSee('Releasing');
+ $this->get('/docs/mobile/4/publishing/android')->assertOk()->assertSee('Play Store');
+ $this->get('/docs/mobile/4/publishing/ios')->assertOk()->assertSee('App Store');
+ }
+
+ public function test_digging_deeper_slug_maps_back_to_concepts_on_the_stable_version(): void
+ {
+ $this->get('/docs/mobile/3/digging-deeper/security')
+ ->assertRedirect('/docs/mobile/3/concepts/security');
+ }
+
+ public function test_the_basics_routing_and_layouts_pages(): void
+ {
+ // Navigation was renamed to Routing; the old slug forwards to it.
+ $this->get('/docs/mobile/4/the-basics/navigation')
+ ->assertRedirect('/docs/mobile/4/the-basics/routing');
+
+ // Routing and Layouts now render directly in The Basics.
+ $this->get('/docs/mobile/4/the-basics/routing')->assertOk();
+ $this->get('/docs/mobile/4/the-basics/layouts')->assertOk();
+ }
+
+ public function test_moved_core_builtin_docs_redirect_from_plugins_core_to_the_basics(): void
+ {
+ $this->get('/docs/mobile/4/plugins/core/device')
+ ->assertRedirect('/docs/mobile/4/the-basics/device');
+ }
+
+ public function test_vibe_docs_moved_to_websockets_with_a_core_plugin_shortcut(): void
+ {
+ // The old Vibe plugin doc now lives at Digging Deeper > WebSockets.
+ $this->get('/docs/mobile/4/plugins/vibe')
+ ->assertRedirect('/docs/mobile/4/digging-deeper/websockets');
+ $this->get('/docs/mobile/4/digging-deeper/websockets')->assertOk();
+
+ // The Core Plugins shortcut forwards to the plugin directory page.
+ $this->get('/docs/mobile/4/plugins/core/vibe')
+ ->assertRedirect('/plugins/nativephp/mobile-vibe');
+ }
+
+ public function test_dialog_page_uses_the_dialogs_slug(): void
+ {
+ // Old plugins/core slug forwards straight to the new dialogs page.
+ $this->get('/docs/mobile/4/plugins/core/dialog')
+ ->assertRedirect('/docs/mobile/4/the-basics/dialogs');
+
+ // The old the-basics/dialog slug forwards to the-basics/dialogs.
+ $this->get('/docs/mobile/4/the-basics/dialog')
+ ->assertRedirect('/docs/mobile/4/the-basics/dialogs');
+
+ // The renamed page renders.
+ $this->get('/docs/mobile/4/the-basics/dialogs')
+ ->assertOk()
+ ->assertSee('Dialogs');
+ }
+
+ public function test_flattened_digging_deeper_and_architecture_pages_render(): void
+ {
+ // The former SuperNative introduction, now the SuperNative page in Architecture.
+ $this->get('/docs/mobile/4/architecture/super-native')
+ ->assertOk()
+ ->assertSee('SuperNative');
+
+ // A flattened former sub-page and an original concept page.
+ $this->get('/docs/mobile/4/digging-deeper/data-binding')
+ ->assertOk();
+
+ $this->get('/docs/mobile/4/digging-deeper/security')
+ ->assertOk();
+ }
}
diff --git a/tests/Unit/DocsVersionServiceTest.php b/tests/Unit/DocsVersionServiceTest.php
index bd010d3b..8bbd279e 100644
--- a/tests/Unit/DocsVersionServiceTest.php
+++ b/tests/Unit/DocsVersionServiceTest.php
@@ -105,4 +105,72 @@ public function it_points_to_the_latest_version_of_getting_started_introduction_
$this->assertEquals($expected, $url);
}
+
+ #[Test]
+ public function it_maps_renamed_concepts_pages_to_digging_deeper_in_v4_and_back_in_older_versions(): void
+ {
+ // Old slug resolves forward on v4 (the version the rename happened in).
+ $this->assertSame(
+ 'digging-deeper/security',
+ $this->docsVersionService->resolvePageForVersion('mobile', 4, 'concepts/security'),
+ );
+
+ // New slug resolves back to the old slug when targeting an older version.
+ $this->assertSame(
+ 'concepts/security',
+ $this->docsVersionService->resolvePageForVersion('mobile', 3, 'digging-deeper/security'),
+ );
+ }
+
+ #[Test]
+ public function it_maps_deployment_to_the_publishing_section_across_versions(): void
+ {
+ // Older versions keep deployment under getting-started; on v4 it forwards to Publishing.
+ $this->assertSame(
+ 'publishing/introduction',
+ $this->docsVersionService->resolvePageForVersion('mobile', 4, 'getting-started/deployment'),
+ );
+
+ // Switching the new Publishing intro back to an older version lands on deployment.
+ $this->assertSame(
+ 'getting-started/deployment',
+ $this->docsVersionService->resolvePageForVersion('mobile', 3, 'publishing/introduction'),
+ );
+ }
+
+ #[Test]
+ public function it_flattens_super_native_pages_into_the_digging_deeper_section_in_v4(): void
+ {
+ // The old SuperNative introduction becomes the SuperNative page in Architecture.
+ $this->assertSame(
+ 'architecture/super-native',
+ $this->docsVersionService->resolvePageForVersion('mobile', 4, 'super-native/introduction'),
+ );
+
+ // Some former SuperNative pages moved into The Basics.
+ $this->assertSame(
+ 'the-basics/routing',
+ $this->docsVersionService->resolvePageForVersion('mobile', 4, 'super-native/navigation'),
+ );
+ $this->assertSame(
+ 'the-basics/layouts',
+ $this->docsVersionService->resolvePageForVersion('mobile', 4, 'super-native/layouts'),
+ );
+
+ // The rest became direct Digging Deeper pages.
+ $this->assertSame(
+ 'digging-deeper/data-binding',
+ $this->docsVersionService->resolvePageForVersion('mobile', 4, 'super-native/data-binding'),
+ );
+
+ // Resolves back to the old paths when targeting an older version.
+ $this->assertSame(
+ 'super-native/introduction',
+ $this->docsVersionService->resolvePageForVersion('mobile', 3, 'architecture/super-native'),
+ );
+ $this->assertSame(
+ 'super-native/layouts',
+ $this->docsVersionService->resolvePageForVersion('mobile', 3, 'the-basics/layouts'),
+ );
+ }
}