- Getting Started
- Architecture
- The Basics
- Digging Deeper
-
EDGE Components
- Introduction
- Accordion
- Activity Indicator
- Badge
- Bottom Navigation
- Bottom Sheet
- Button
- Button Group
- Canvas
- Carousel
- Checkbox
- Chip
- Column
- Divider
- Floating Action Button
- Gesture Area
- Icon
- Image
- Lazy Grid
- Layout & Styling
- List
- Menus
- Modal
- Pressable
- Progress Bar
- Radio Group
- Pager
- Refreshable
- Row
- Scroll View
- Select
- Shapes
- Side Navigation
- Slider
- Spacer
- Stack
- Tab Row
- Text
- Text Input
- Toggle
- Top Bar
- Virtual List
- Web View
- Plugins
- Testing
- Publishing Your App
Routing
Overview#
Native screens form a stack, exactly like a native iOS UINavigationController or an Android Activity stack. From
inside a NativeComponent, you push, pop, and replace screens using methods on $this.
Registering routes#
Native routes are registered similarly to Livewire routes, but instead of using Route::livewire() you use the
Route::native() macro:
use App\NativeComponents\Home;use App\NativeComponents\ItemDetail; Route::native('/', Home::class);Route::native('/item/{id}', ItemDetail::class);
You can put these routes anywhere that makes sense for your application, but you may find that creating a
routes/mobile.php to keep them all together is a clean option.
Route parameters work just like Laravel web routes — {id} matches a path segment and is exposed to the screen
through $this->param('id').
See Layouts for how to attach shared chrome to a route or group of routes, and
Middleware for guarding screens with ->middleware().
Pushing a new screen#
$this->navigate('/item/42');
By default a push slides in from the right. Pass arbitrary data along with the navigation:
$this->navigate('/item/42', ['source' => 'home-feed']);
The next screen reads the data with $this->data('source').
Going back#
$this->back();
Pops the current screen off the stack and returns to whatever was beneath it. The default transition is a slide-out to the right.
Replacing the current screen#
$this->replace('/login');
Pops the current screen and pushes a new one in its place — useful after sign-in / sign-out or for in-place tab swaps. The default transition is a fade.
Exiting to the web view#
$this->exitToWeb('/dashboard');
Tear down the native UI stack and load the given URL in the web view. Use this for screens that aren't part of the native experience.
Custom transitions#
Chain transition() after a navigation method to override the default animation:
use Native\Mobile\Edge\Transition; $this->navigate('/item/42')->transition(Transition::SlideFromBottom);
Available cases:
Transition::SlideFromRight- default fornavigate()Transition::SlideFromLeft- default forback()Transition::SlideFromBottom- modal-style presentationTransition::Fade- default forreplace()Transition::FadeFromBottom- subtle vertical fadeTransition::ScaleFromCenter- zoom-in effectTransition::ParallaxPush- incoming screen slides in from the right while the outgoing screen drifts partially left underneathTransition::Morph4.7 - cross-fade while elements that share arefmove between the two screens. See Morphing elements between screensTransition::None- swap with no animation
Navigating from Blade#
The @navigate directive triggers navigation directly from your view, with no component method required. It works on
any node, so any element can become a navigation target.
The short form takes a route in single quotes:
<native:pressable @navigate='/item/42' class="w-full p-4"> <native:text>View item</native:text></native:pressable>
To pass data along with the navigation, use the args form — a route followed by an array:
<native:pressable @navigate="'/item/42', ['source' => 'home-feed']"> <native:text>View item</native:text></native:pressable>
The next screen reads the data with $this->data('source'), exactly as with $this->navigate().
Double quotes hold a PHP expression rather than a plain route, so @navigate="/item/42" won't compile. For a
dynamic route, build the string in PHP: @navigate="'/item/' . $item->id".
Transition modifiers#
Add a transition as a modifier to override the default push animation:
<native:pressable @navigate.slideFromBottom='/item/42'> <native:text>Present modally</native:text></native:pressable>
Available transition modifiers:
fadeslideFromRightslideFromLeftslideFromBottomfadeFromBottomscaleFromCenterparallaxPushmorph4.7-none
Action modifiers#
Modifiers also select the navigation action. Use these in place of a route to back, or combine them with a route
to replace or exitToWeb:
<native:pressable @navigate.back> <native:text>Go back</native:text></native:pressable> <native:pressable @navigate.replace='/login'> <native:text>Sign out</native:text></native:pressable> <native:pressable @navigate.exitToWeb='/dashboard'> <native:text>Open dashboard</native:text></native:pressable>
Transition and action modifiers can be combined — @navigate.replace.fade='/login'.
Morphing elements between screens#
4.7 The morph transition carries an element from where it sits on one screen to where it sits on the next. A thumbnail grows into a header image, or an avatar slides into place on a profile page. If you've used the web's View Transitions API, it's the same idea.
Give the element the same ref on both screens, then navigate with the morph modifier:
{{-- The album list --}}@foreach ($albums as $album) <native:pressable @navigate.morph="'/album/' . $album->id"> <native:image :ref="'album-' . $album->id" :src="$album->cover" class="w-16 h-16 rounded-xl" /> </native:pressable>@endforeach
{{-- The album screen --}}<native:column class="w-full"> <native:image :ref="'album-' . $album->id" :src="$album->cover" class="w-full h-80" /> <native:pressable @navigate.back.morph> <native:text>Back</native:text> </native:pressable></native:column>
Tapping an album cross-fades the two screens while the cover moves and grows from the small tile into the header.
Going back with @navigate.back.morph plays it in reverse.
From a component, pass Transition::Morph:
use Native\Mobile\Edge\Transition; $this->navigate('/album/3')->transition(Transition::Morph);
Shaping the morph#
These attributes change how an element travels. They're read from the element on the screen you're navigating to, so put them on both elements if you want the same motion in both directions.
morph- what moves.frame(the default) moves and resizes the element.positionmoves it at its final size.sizeresizes it in place, at its final position.nonestops the element morphing.morph-duration- length in milliseconds. The default is350, the same as the cross-fade behind it.morph-easing- the curve:linear,ease-in,ease-out,ease-in-out(the default) orspring.
<native:column ref="now-playing" morph="position" morph-duration="500" morph-easing="spring"> ...</native:column>
Which elements morph#
- An element morphs only when its
refis on both screens. Everything else cross-fades with its screen. - Elements only morph under the
morphtransition. Arefthat appears on both screens does nothing during a slide or a fade. - Keep each
refunique within a screen. If two elements share one, you can't be sure which of them moves. refis also how tests target elements. If you've reused a testrefon two screens and don't want those elements to morph, addmorph="none"to either of them.
Native chrome#
A morph needs one whole screen to replace another. Inside native chrome that doesn't always happen. Pushing from one screen with a native top bar to another, or switching between tabs of a bottom nav, is animated by the platform's own navigation, and nothing morphs.
Morphs do run between screens that have no native chrome, and whenever the chrome changes, such as a tab screen opening a detail screen that has a top bar instead. See Layouts for how screens get their chrome.
Reading params and data#
class ItemDetail extends NativeComponent{ public function mount(): void { $id = $this->param('id'); // from the route URI $source = $this->data('source', 'unknown'); // from navigate()'s second arg // ... }}
Both accessors take an optional default that's returned when the key is missing.
Resolving named routes#
If you've named your routes you can use route() on the component to resolve them to URIs:
$this->navigate($this->route('listing.show', ['id' => 5]));
This delegates to Laravel's URL generator with absolute: false.
Customizing device-back behavior#
By default, the device back button (Android) pops the navigation stack. Override onBackPressed() to add custom
behavior:
class CheckoutForm extends NativeComponent{ public bool $dirty = false; public function onBackPressed(): void { if ($this->dirty) { $this->showDiscardConfirmation(); return; } $this->back(); }}
If the back press should ultimately still pop, call $this->back() from your override.
in no time