- Getting Started
- The Basics
- Concepts
- SuperNative
-
EDGE Components
- Introduction
- Activity Indicator
- Badge
- Bottom Navigation
- Bottom Sheet
- Button
- Button Group
- Canvas
- Carousel
- Checkbox
- Chip
- Column
- Divider
- Gesture Area
- Icon
- Icons
- Image
- Layout & Styling
- List
- Menus
- Modal
- Pressable
- Progress Bar
- Radio Group
- Row
- Scroll View
- Select
- Shapes
- Side Navigation
- Slider
- Spacer
- Stack
- Tab Row
- Text
- Text Input
- Toggle
- Top Bar
- Virtual List
- Web View
- Plugins
- Testing
- Architecture
You're viewing pre-release documentation — version 4.x is in beta
Features, APIs and behaviour may change before the stable release. View the stable version (3.x)
Vibe (Websockets)
Overview#
Vibe brings live server events into your NativePHP Mobile app over the Pusher protocol — so it works with Vask, Laravel Reverb, or Pusher without changing your code. Your components subscribe to channels and react to broadcasts, exactly like Laravel Echo does in the browser.
The websocket lives on the native side (Swift/Kotlin, via the official Pusher SDKs — PusherSwift on iOS,
pusher-java-client on Android). PHP is purely a client subscriber: it declares what to subscribe to and handles the
events that arrive. Mobile apps never broadcast — they only listen.
Events arrive as native events and re-render the component that subscribed to them.
use Nativephp\Vibe\Facades\Vibe;
Installation#
composer require nativephp/mobile-vibephp artisan native:plugin:register nativephp/mobile-vibe
Then rebuild your app so the plugin's native code is compiled in:
php artisan native:run
Configuration#
Vibe reads the standard Laravel PUSHER_* variables from your app's .env. These are bundled into the app at build
time.
PUSHER_APP_KEY=your-app-keyPUSHER_HOST=wss.vask.dev # the WEBSOCKET hostPUSHER_PORT=443PUSHER_SCHEME=https
For private and presence channels, also point Vibe at your backend's authorization endpoint:
VIBE_AUTH_ENDPOINT=https://your-backend.example.com/api/v1/broadcasting/auth
Subscribing to channels#
Subscribe inside a NativeComponent's mount() method. The closures you pass run as component methods, so $this
refers to the component and any property you assign triggers a re-render.
Subscriptions are torn down automatically when the component unmounts — leave the screen and you leave the channel (or presence room).
Public channels#
Vibe::channel() subscribes to a public channel. Chain ->on() to react to a broadcast. The $event argument is a
plain object of the broadcast payload, so you read fields with $event->field.
use Nativephp\Vibe\Facades\Vibe; class OrderStatus extends NativeComponent{ public string $status = 'pending'; public function mount(): void { Vibe::channel('orders')->on('OrderShipped', function ($event) { $this->status = $event->status; // $this is the component }); }}
The event name must match what your server broadcasts.
Authentication for private and presence channels#
Private (private-) and presence (presence-) channels require a signed authorization from your remote Laravel
backend — a /broadcasting/auth endpoint guarded by auth:sanctum, with channel authorization callbacks. The app
authenticates to it with a bearer token.
Register a token resolver once (for example in AppServiceProvider::boot()). It returns the current bearer token
and is called at subscribe time.
use Nativephp\Vibe\Facades\Vibe;use Native\Mobile\Facades\SecureStorage; // AppServiceProvider::boot()Vibe::resolveTokenUsing(fn () => SecureStorage::get('api_token'));
After a re-login or token refresh, push the fresh token to the live connection so subsequent subscriptions authorize with it:
Vibe::withToken($freshToken);
Private channels#
Vibe::private() auto-prefixes the channel name with private- and authorizes it through your endpoint. Otherwise it
behaves like a public channel.
use Nativephp\Vibe\Facades\Vibe; class OrderStatus extends NativeComponent{ public string $status = 'pending'; public function mount(): void { Vibe::private('orders.42')->on('OrderShipped', function ($event) { $this->status = $event->status; }); }}
Presence channels#
Vibe::presence() auto-prefixes the name with presence- and, on top of events, tracks who is in the room. The auth
response carries channel_data (a member's id and info), which Vibe surfaces through three lifecycle callbacks:
->here()runs once on join with the full roster->joining()runs when someone joins->leaving()runs when someone leaves
Each member is an array shaped ['id' => ..., 'info' => [...]]. You can still chain ->on() to handle broadcasts on the
same channel.
use Nativephp\Vibe\Facades\Vibe; class ChatRoom extends NativeComponent{ public array $online = []; public array $messages = []; public function mount(): void { Vibe::presence('room.1') ->here(fn (array $members) => $this->online = $members) // each: ['id' => ..., 'info' => [...]] ->joining(fn (array $member) => $this->online[] = $member) ->leaving(function (array $member) { $this->online = array_values(array_filter( $this->online, fn ($m) => $m['id'] !== $member['id'], )); }) ->on('MessageSent', fn ($event) => $this->messages[] = $event->body); }}
Handling events with #[OnEcho]#
As an alternative to the fluent ->on() callback, you can annotate a public component method with the #[OnEcho]
attribute. Method parameters are bound by name from the broadcast payload.
use Nativephp\Vibe\Facades\Vibe;use Nativephp\Vibe\Attributes\OnEcho; class OrderStatus extends NativeComponent{ public string $status = 'pending'; public function mount(): void { Vibe::channel('orders'); // subscribe; no ->on() needed } #[OnEcho('OrderShipped')] public function whenShipped(string $status): void { $this->status = $status; }}
Methods#
All methods are called on the Vibe facade. channel(), private(), presence(), and subscribe() return a
PendingSubscription you can chain.
channel()#
Subscribes to a public channel.
Parameters:
string $name- The channel name
Returns: PendingSubscription
private()#
Subscribes to a private channel, auto-prefixing the name with private-. Requires authentication.
Parameters:
string $name- The channel name (without theprivate-prefix)
Returns: PendingSubscription
presence()#
Subscribes to a presence channel, auto-prefixing the name with presence-. Requires authentication and tracks members.
Parameters:
string $name- The channel name (without thepresence-prefix)
Returns: PendingSubscription
subscribe()#
Low-level primitive. You pass the full, already-prefixed channel name.
Parameters:
string $fullChannelName- The complete channel name, including anyprivate-/presence-prefix
Returns: PendingSubscription
resolveTokenUsing()#
Registers the bearer-token resolver used to authorize private and presence subscriptions. Register it once, typically in
AppServiceProvider::boot(). It is called at subscribe time.
Parameters:
Closure $resolver- Returns the current bearer token (e.g. from secure storage)
Returns: void
withToken()#
Pushes a fresh bearer token to the live connection after a re-login or refresh.
Parameters:
string $token- The new bearer token
Returns: void
PendingSubscription#
The chainable object returned by the subscription methods above.
on()#
Runs $callback on each matching broadcast. The $event argument is a plain object of the payload; read fields with
$event->field.
Parameters:
string $event- The broadcast event name (match your server'sbroadcastAs())Closure $callback- Receives$event, the payload object
Returns: PendingSubscription
here()#
Presence only. Runs once when you join, with the full member roster.
Parameters:
Closure $callback- Receivesarray $members; each member is['id' => ..., 'info' => [...]]
Returns: PendingSubscription
joining()#
Presence only. Runs when a member joins.
Parameters:
Closure $callback- Receivesarray $member, shaped['id' => ..., 'info' => [...]]
Returns: PendingSubscription
leaving()#
Presence only. Runs when a member leaves.
Parameters:
Closure $callback- Receivesarray $member, shaped['id' => ..., 'info' => [...]]
Returns: PendingSubscription
Notes#
- 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.
- 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 channels and presence rooms.
- Private/presence require a remote backend: Signing happens on your remote Laravel app at
/broadcasting/auth(behindauth:sanctum) with channel authorization callbacks. The device never holds the app secret.
in no time