This skill should be used when the user asks about "livewire nativephp", "livewire native event", "#[OnNative]", "livewire camera", "livewire component native", "livewire geolocation", "livewire biometrics", "handle native event in livewire", "livewire photo upload", "livewire scanner", or needs to integrate NativePHP native functionality into Livewire components.
/plugin marketplace add NativePHP/ClaudePlugins/plugin install nativephp-mobile@nativephp-mobile-pluginThis skill inherits all available tools. When active, it can use any tool Claude has access to.
This skill provides guidance for integrating NativePHP native functionality into Livewire components, including event handling, API usage, and best practices.
Livewire components are the primary way to build interactive NativePHP Mobile apps. Native events are dispatched via JavaScript injection and can be listened to using Livewire attributes.
NativePHP provides a custom #[OnNative] attribute that MUST be used for listening to native events. This is cleaner and more reliable than the raw Livewire #[On] attribute.
use Livewire\Component;
use Native\Mobile\Attributes\OnNative;
use Native\Mobile\Events\Camera\PhotoTaken;
use Native\Mobile\Events\Camera\PhotoCancelled;
class PhotoUploader extends Component
{
public ?string $photoPath = null;
#[OnNative(PhotoTaken::class)]
public function handlePhotoTaken(string $path, string $mimeType = 'image/jpeg', ?string $id = null)
{
$this->photoPath = $path;
// Process the photo
}
#[OnNative(PhotoCancelled::class)]
public function handlePhotoCancelled(bool $cancelled = true, ?string $id = null)
{
session()->flash('info', 'Photo capture was cancelled');
}
}
IMPORTANT: Always use #[OnNative(EventClass::class)] - NEVER use the raw #[On('native:...')] syntax. The OnNative attribute automatically handles the native: prefix and event class resolution.
namespace App\Livewire;
use Livewire\Component;
use Native\Mobile\Attributes\OnNative;
use Native\Mobile\Events\Camera\PhotoTaken;
use Native\Mobile\Events\Camera\PhotoCancelled;
use Native\Mobile\Events\Gallery\MediaSelected;
use Native\Mobile\Facades\Camera;
class AvatarUploader extends Component
{
public ?string $avatarPath = null;
public ?string $avatarUrl = null;
public function takePhoto()
{
Camera::getPhoto()
->id('avatar-' . auth()->id())
->remember()
->start();
}
public function pickFromGallery()
{
Camera::pickImages()
->images()
->single()
->id('avatar-gallery')
->remember()
->start();
}
#[OnNative(PhotoTaken::class)]
public function handlePhotoTaken(string $path, string $mimeType, ?string $id)
{
if (str_starts_with($id, 'avatar-')) {
$this->processAvatar($path);
}
}
#[OnNative(MediaSelected::class)]
public function handleMediaSelected(bool $success, array $files, int $count, ?string $error, bool $cancelled, ?string $id)
{
if ($success && $count > 0) {
$this->processAvatar($files[0]['path']);
}
}
#[OnNative(PhotoCancelled::class)]
public function handlePhotoCancelled()
{
// User cancelled - no action needed
}
private function processAvatar(string $path)
{
// Move to permanent storage
$filename = 'avatars/' . auth()->id() . '_' . time() . '.jpg';
Storage::disk('public')->put($filename, file_get_contents($path));
$this->avatarPath = $filename;
$this->avatarUrl = Storage::disk('public')->url($filename);
// Update user record
auth()->user()->update(['avatar' => $filename]);
}
public function render()
{
return view('livewire.avatar-uploader');
}
}
Blade template:
<div>
<div class="avatar-container">
@if($avatarUrl)
<img src="{{ $avatarUrl }}" alt="Avatar" class="avatar">
@else
<div class="avatar-placeholder">No Photo</div>
@endif
</div>
<div class="button-group">
<button wire:click="takePhoto" type="button">
Take Photo
</button>
<button wire:click="pickFromGallery" type="button">
Choose from Gallery
</button>
</div>
</div>
namespace App\Livewire;
use Livewire\Component;
use Native\Mobile\Attributes\OnNative;
use Native\Mobile\Events\Scanner\CodeScanned;
use Native\Mobile\Facades\Scanner;
class ProductScanner extends Component
{
public array $scannedProducts = [];
public function startScanning()
{
Scanner::scan()
->prompt('Scan product barcode')
->formats(['ean13', 'qr', 'code128'])
->continuous(true)
->id('product-scanner')
->scan();
}
#[OnNative(CodeScanned::class)]
public function handleCodeScanned(string $data, string $format, ?string $id)
{
// Look up product
$product = Product::where('barcode', $data)->first();
if ($product) {
$this->scannedProducts[] = [
'barcode' => $data,
'name' => $product->name,
'price' => $product->price,
];
} else {
session()->flash('warning', "Unknown barcode: {$data}");
}
}
public function render()
{
return view('livewire.product-scanner');
}
}
namespace App\Livewire;
use Livewire\Component;
use Native\Mobile\Attributes\OnNative;
use Native\Mobile\Events\Biometric\Completed;
use Native\Mobile\Facades\Biometrics;
class SecureAction extends Component
{
public bool $authenticated = false;
public string $pendingAction = '';
public function performSecureAction(string $action)
{
$this->pendingAction = $action;
Biometrics::prompt()
->id('secure-' . $action)
->prompt();
}
#[OnNative(Completed::class)]
public function handleBiometricResult(bool $success, ?string $id)
{
if ($success) {
$this->authenticated = true;
// Execute the pending action
match($this->pendingAction) {
'delete-account' => $this->deleteAccount(),
'export-data' => $this->exportData(),
'change-password' => $this->showPasswordForm(),
default => null,
};
} else {
session()->flash('error', 'Authentication failed');
}
$this->pendingAction = '';
}
// ... action methods
}
namespace App\Livewire;
use Livewire\Component;
use Native\Mobile\Attributes\OnNative;
use Native\Mobile\Events\Geolocation\LocationReceived;
use Native\Mobile\Events\Geolocation\PermissionStatusReceived;
use Native\Mobile\Facades\Geolocation;
class LocationTracker extends Component
{
public ?float $latitude = null;
public ?float $longitude = null;
public bool $hasPermission = false;
public function mount()
{
Geolocation::checkPermissions()->get();
}
public function requestLocation()
{
if (!$this->hasPermission) {
Geolocation::requestPermissions()->get();
return;
}
Geolocation::getCurrentPosition()
->fineAccuracy(true)
->id('current-location')
->get();
}
#[OnNative(PermissionStatusReceived::class)]
public function handlePermissionStatus(string $location, string $coarseLocation, string $fineLocation)
{
$this->hasPermission = in_array($fineLocation, ['granted', 'limited']);
}
#[OnNative(LocationReceived::class)]
public function handleLocationReceived(
bool $success,
?float $latitude,
?float $longitude,
?float $accuracy,
?int $timestamp,
?string $provider,
?string $error
) {
if ($success) {
$this->latitude = $latitude;
$this->longitude = $longitude;
} else {
session()->flash('error', $error ?? 'Failed to get location');
}
}
}
namespace App\Livewire;
use Livewire\Component;
use Native\Mobile\Attributes\OnNative;
use Native\Mobile\Events\Alert\ButtonPressed;
use Native\Mobile\Facades\Dialog;
class ItemManager extends Component
{
public ?int $pendingDeleteId = null;
public function confirmDelete(int $id)
{
$this->pendingDeleteId = $id;
Dialog::alert(
'Confirm Delete',
'Are you sure you want to delete this item?',
['Cancel', 'Delete']
)
->id('delete-' . $id)
->show();
}
#[OnNative(ButtonPressed::class)]
public function handleDialogResponse(int $index, string $label, ?string $id)
{
if (str_starts_with($id, 'delete-') && $label === 'Delete') {
$itemId = (int) str_replace('delete-', '', $id);
Item::find($itemId)?->delete();
session()->flash('success', 'Item deleted');
}
$this->pendingDeleteId = null;
}
}
namespace App\Livewire;
use Livewire\Component;
use Native\Mobile\Attributes\OnNative;
use Native\Mobile\Events\Microphone\MicrophoneRecorded;
use Native\Mobile\Events\Microphone\MicrophoneCancelled;
use Native\Mobile\Facades\Microphone;
class VoiceNote extends Component
{
public bool $isRecording = false;
public ?string $recordingPath = null;
public function startRecording()
{
Microphone::record()
->id('voice-note')
->remember()
->start();
$this->isRecording = true;
}
public function stopRecording()
{
Microphone::stop();
$this->isRecording = false;
}
#[OnNative(MicrophoneRecorded::class)]
public function handleRecordingComplete(string $path, string $mimeType, ?string $id)
{
$this->recordingPath = $path;
$this->isRecording = false;
// Save to storage
$filename = 'recordings/' . auth()->id() . '_' . time() . '.m4a';
Storage::put($filename, file_get_contents($path));
}
#[OnNative(MicrophoneCancelled::class)]
public function handleRecordingCancelled()
{
$this->isRecording = false;
}
}
namespace App\Livewire;
use Livewire\Component;
use Native\Mobile\Attributes\OnNative;
use Native\Mobile\Events\PushNotification\TokenGenerated;
use Native\Mobile\Facades\PushNotifications;
class NotificationSettings extends Component
{
public bool $enrolled = false;
public function enrollForNotifications()
{
PushNotifications::enroll()
->id('main')
->enroll();
}
#[OnNative(TokenGenerated::class)]
public function handleTokenGenerated(string $token, ?string $id)
{
// Store token on your server
auth()->user()->update(['push_token' => $token]);
$this->enrolled = true;
session()->flash('success', 'Push notifications enabled');
}
}
Apply safe area handling in your Livewire layout:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
@livewireStyles
</head>
<body class="nativephp-safe-area">
{{ $slot }}
@livewireScripts
</body>
</html>
ALWAYS use #[OnNative(EventClass::class)] - This is the required pattern, not #[On('native:...')]. The OnNative attribute from Native\Mobile\Attributes\OnNative automatically handles the native: prefix.
Track requests with IDs - Correlate async requests with responses:
Camera::getPhoto()->id('unique-id')->remember()->start();
Handle cancellations - Always handle cancelled events gracefully
Check permissions first - For location, camera, etc., check/request permissions before use
Use session flashing - For user feedback on native operations
Clean up temporary files - Move captured media to permanent storage
All NativePHP Facades are in Native\Mobile\Facades\:
| Facade | Description |
|---|---|
Biometrics | Face ID / fingerprint authentication |
Browser | Open URLs, in-app browser, OAuth |
Camera | Photo capture, video recording, gallery picker |
Device | Device info, vibration, flashlight |
Dialog | Native alerts and toasts |
File | File operations |
Geolocation | GPS location, permissions |
Haptics | Haptic feedback |
Microphone | Audio recording |
Network | Network status |
PushNotifications | Push notification enrollment |
Scanner | QR/barcode scanning |
SecureStorage | Encrypted key-value storage |
Share | Native share sheet |
System | Platform detection, app settings |
All events are in Native\Mobile\Events\:
| Event | Parameters |
|---|---|
Camera\PhotoTaken | string $path, string $mimeType, ?string $id |
Camera\PhotoCancelled | bool $cancelled, ?string $id |
Camera\VideoRecorded | string $path, string $mimeType, ?string $id |
Camera\VideoCancelled | bool $cancelled, ?string $id |
Gallery\MediaSelected | bool $success, array $files, int $count, ?string $error, bool $cancelled, ?string $id |
Scanner\CodeScanned | string $data, string $format, ?string $id |
Biometric\Completed | bool $success, ?string $id |
Alert\ButtonPressed | int $index, string $label, ?string $id |
Geolocation\LocationReceived | bool $success, ?float $latitude, ?float $longitude, ?float $accuracy, ?int $timestamp, ?string $provider, ?string $error, ?string $id |
Geolocation\PermissionStatusReceived | string $location, string $coarseLocation, string $fineLocation, ?string $id |
Geolocation\PermissionRequestResult | (varies) |
Microphone\MicrophoneRecorded | string $path, string $mimeType, ?string $id |
Microphone\MicrophoneCancelled | bool $cancelled, ?string $id |
PushNotification\TokenGenerated | string $token, ?string $id |
App\UpdateInstalled | (varies) |
For detailed Livewire integration:
https://nativephp.com/docs/mobile/2/the-basics/eventshttps://nativephp.com/docs/mobile/2/apis/overviewUse WebFetch to retrieve the latest patterns and examples.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.