Build desktop & mobile apps with Laravel + Livewire. No Electron. No React Native. Just PHP.
Configuration • Components • Directives & Attributes • Architecture • Navigate • Plugins • Push • Analytics • Media • Animations • Lifecycle • Database • Cache • Languages • Filesystem • Build • Scheduler • Auto-Update • Publish • MCP (AI agents)
NativeBlade lets Laravel developers build desktop and mobile apps using only PHP and Blade. Your entire Laravel + Livewire application runs inside a PHP WebAssembly runtime, wrapped in a Tauri 2 shell. No JavaScript frameworks. No API layers. Just the Laravel you already know.
- Pure Laravel — Routes, Livewire components, Blade templates, Eloquent (SQLite)
- Tiny Bundle — A full Laravel + Livewire app compresses to ~6 MB gzipped (see BUILD.md)
- Native Shell — Top bar, bottom navigation, drawer, modal, tray — all outside the WebView
- Native APIs — Dialogs, notifications, camera, geolocation, haptics, biometric, NFC, barcode
- Desktop — Windows, macOS, Linux with native menus and system tray
- Mobile — Android & iOS with status bar, safe area, orientation control
- Push Notifications — Server-pushed notifications via FCM (Android) and APNS (iOS) that wake the app even when closed
- Analytics: Firebase Analytics via the native SDK (events, screens, user properties), with auto screen tracking and consent controls
- Animations — 90+ Animate.css animations + custom NativeBlade animations via
nb-animationattribute - Offline-First — SQLite persisted to IndexedDB, works without a server
- Hot Reload — Vite HMR for instant feedback during development, plus
--buildmode to preview the production bundle locally - Icons — 3,024 Phosphor Icons (regular + fill) included
- Custom Fonts — Offline font loading via base64 embedding
- Page Transitions — Fade, slide, zoom, flip, bounce, blur — powered by Animate.css
- AI-Ready — Built-in MCP server so Claude Code, Cursor, and Windsurf can introspect your live project (declared plugins, facade methods, framework docs) instead of guessing
- PHP 8.3, 8.4, or 8.5 (with GD extension)
- Laravel 11, 12, or 13
- Livewire 3
- Node.js 20+
- Rust — install here
# 1. Create a new Laravel project
composer create-project laravel/laravel my-app
cd my-app
# 2. Install NativeBlade
composer require nativeblade/nativeblade
php artisan nativeblade:install
# 3. Build the frontend
npm run build
# 4. Launch the desktop app
php artisan nativeblade:dev✓ src-tauri/ — Tauri project
✓ layouts/ — Blade layouts with shell support
✓ vite.wasm.config.js — Vite config for WASM bundling
✓ AppServiceProvider — NativeBlade config
✓ Demo app — Login, Trail, Lesson, Rank, Profile
The first run compiles the Rust binary, which takes a few minutes. Subsequent runs are fast.
php artisan nativeblade:add android
php artisan nativeblade:dev --platform=android --host=192.168.0.10
php artisan nativeblade:add ios
php artisan nativeblade:dev --platform=ios --host=192.168.0.10Mobile requires
--host=<your-local-ip>so the device/emulator can reach the Vite dev server running on your machine. Replace192.168.0.10with your computer's LAN IP (find it withipconfigon Windows orifconfig/ip addron macOS/Linux).localhostwon't work because the phone can't reach your machine through it.
A companion app for iOS and Android that loads any NativeBlade dev bundle by URL or QR scan. Install once, then point it at your nativeblade:dev server to preview your app on a real device with no Xcode / Android Studio build needed.
php artisan nativeblade:dev --platform=portal --host=192.168.0.10Open the Portal app on your phone, scan the QR shown in your terminal (or paste the URL), and your Laravel + Livewire app loads in seconds. Switch between projects by changing the URL — no rebuild required.
Test the exact bundle that will ship — without the Vite dev server and without running a full nativeblade:build:
php artisan nativeblade:dev --build
php artisan nativeblade:dev --platform=android --build
php artisan nativeblade:dev --platform=ios --buildBuilds the frontend once, points Tauri at dist-wasm/, and launches the app. Ideal for validating the real production payload or iterating on Rust/native shell without HMR in the way. See BUILD.md.
NativeBlade ships only the shell + runtime, not a styled component library. Pick a UI kit that fits the form factor:
| Form factor | Recommended kit | Install |
|---|---|---|
| Mobile (iOS + Android) | nativeblade/ui-mobile — Konsta-inspired Blade components with iOS and Material themes auto-detected per platform |
composer require nativeblade/ui-mobile |
| Desktop (Windows, macOS, Linux) | Flux UI — official Livewire UI kit by Caleb Porzio. Free core + paid Pro | composer require livewire/flux |
Any Livewire-compatible Blade UI library works (Filament, mary-ui, TallStackUI, Wireui, etc.). The shell, router, and bridges are component-agnostic.
┌─────────────────────────────────────────────┐
│ Tauri Shell (native window) │
│ ┌──────────────────────────────┐ │
│ │ Top Bar / Header │ ← Shell │
│ ├──────────────────────────────┤ │
│ │ │ │
│ │ iframe (srcdoc) │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Laravel + Livewire │ │ ← Your │
│ │ │ via PHP WebAssembly │ │ App │
│ │ └──────────────────────┘ │ │
│ │ │ │
│ ├──────────────────────────────┤ │
│ │ Bottom Navigation │ ← Shell │
│ └──────────────────────────────┘ │
└─────────────────────────────────────────────┘
- Boot — PHP WebAssembly loads your Laravel app (8.3, 8.4, or 8.5)
- Migrate — Pending migrations run automatically
- onBoot — Your startup code runs (license check, data sync, API calls) while splash is visible
- Route — Each navigation runs through Laravel's router inside WASM
- Render — Blade/Livewire HTML is rendered in an iframe
- Intercept — Fetch interceptor routes HTTP requests through WASM
- Bridge — Native actions flow through
postMessage - Schedule — Rust timers execute recurring tasks via Laravel Schedule
- Persist — SQLite syncs to IndexedDB automatically
Migrations run automatically on boot — no php artisan migrate needed. Use standard Laravel migrations and Eloquent:
php artisan make:model Task -m// Migration runs automatically when the app opens
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->boolean('done')->default(false);
$table->timestamps();
});// Eloquent works as usual
Task::create(['title' => 'Buy milk']);
$tasks = Task::where('done', false)->get();
$task->update(['done' => true]);Run code before the app becomes visible. Splash stays up until complete:
NativeBladeConfig::onBoot(function () {
$license = Http::get('https://api.myapp.com/license/check')->json();
NativeBlade::setState('license', $license);
});HTTP Bridge, Storage, Eloquent — everything works inside onBoot. See LIFECYCLE.md.
Laravel Schedule powered by Rust native timers. Define in routes/console.php:
use Illuminate\Support\Facades\Schedule;
Schedule::call(fn () => SyncService::run())->everyFiveMinutes()->name('sync');
Schedule::call(fn () => CacheService::cleanup())->daily()->name('cleanup');All Laravel frequency methods work. Overdue tasks execute on next app open. See LIFECYCLE.md.
use NativeBlade\Facades\NativeBlade;
NativeBlade::setState('auth.user', ['name' => 'John']);
$user = NativeBlade::getState('auth.user');
NativeBlade::forget('auth.user');
NativeBlade::flush();Write log entries from any PHP context (Livewire actions, schedules, routes, mount()) and see them in the Tauri devtools console in real time. Works even when PHP crashes mid-execution — the logs written before the crash are preserved.
use NativeBlade\Facades\NativeBlade;
NativeBlade::log('App started');
NativeBlade::log('Retrying', ['attempt' => 3], 'warn');
NativeBlade::log('Payment failed', ['error' => $e->getMessage()], 'error');
NativeBlade::log('Query ran', ['ms' => 12], 'debug');Each level maps to a console.* method and a color in the [NB:<level>] prefix:
| Level | Console method | Prefix color |
|---|---|---|
info (default) |
console.log |
blue |
warn |
console.warn |
orange |
error |
console.error |
red |
debug |
console.debug |
purple |
The context array is rendered as an expandable object in the browser devtools.
NativeBlade::platform(); // 'windows', 'macos', 'linux', 'android', 'ios'
NativeBlade::isDesktop();
NativeBlade::isMobile();
NativeBlade::isAndroid();
NativeBlade::isIos();NativeBlade::navigate('/dashboard')->toResponse();
NativeBlade::navigate('/', replace: true)->toResponse();<button wire:nb-navigate="/users">Users</button>
<button wire:nb-navigate.replace="/">Home</button>// Middleware
$user = NativeBlade::getState('auth.user');
if (!$user) {
return NativeBlade::navigate('/login')->toResponse();
}
// Login
NativeBlade::setState('auth.user', ['name' => 'Admin', 'email' => $email]);
return NativeBlade::navigate('/', replace: true)->toResponse();
// Logout
NativeBlade::forget('auth.user');
NativeBlade::navigate('/login', replace: true)->toResponse();Laravel's Http facade works transparently — WASM can't make network requests directly, so NativeBlade bridges them through JavaScript:
$response = Http::get('https://api.github.com/users');
// Parallel requests
$responses = NativeBlade::pool(fn ($pool) => [
$pool->get('https://api.com/users'),
$pool->get('https://api.com/posts'),
]);NativeBlade is a client-side runtime, so anything inherently server-side (sending email, processing queues, running cron daemons) is intentionally not built in — your Laravel server handles those, and the client talks to it.
| Works natively | Via bridge | Via your Laravel server | Custom plugin / shell component |
|---|---|---|---|
| Routing, Blade, Livewire | Http facade (REST/GraphQL) |
Queues, jobs, dispatched tasks | WebSockets / Pusher / Reverb |
| Eloquent on SQLite (local) | MySQL / PostgreSQL / MariaDB (remote, via Rust) | Mail (SMTP, Mailgun, SES) | BLE, Bluetooth Classic |
| Middleware, Validation | Native Filesystem (Storage) | Redis, Memcached | Thermal printer (ESC/POS) |
| Collections, Carbon, Eloquent relations | Camera, Gallery, Video, Barcode, NFC | File Storage (S3, R2, GCS) | Payment terminals (TEF) |
| Service Container, Facades | Biometric, Push (FCM/APNS), Haptics | Heavy reports / aggregations | SAT / fiscal printers |
| Localization, Validation rules | Geolocation, Clipboard, Notifications | Auth providers (OAuth, SSO) | Anything else with tauriInvoke |
| Migrations (auto on boot) | Tauri/Rust commands, Upload streaming | ||
| Task Scheduling (Rust timers) | Custom plugins via tauriInvoke |
WebSockets specifically: Livewire's
wire:pollcovers most "real-time" needs in business apps with zero infrastructure. For genuine real-time (chat with typing indicators, collaborative editing), build a shell component that lives outside the iframe, connects to your WS server, and dispatches Livewire events. See PLUGINS.md → Composer plugin discovery.
| Doc | Description |
|---|---|
| CONFIGURATION.md | Desktop, Android, iOS configs, permissions, privacy manifest, transitions |
| ARCHITECTURE.md | Opinionated app structure — Component = Controller, services, state wrappers, push handlers, folder layout |
| NAVIGATE.md | SPA routing — wire:nb-navigate directive, NativeBlade::navigate(), transition and replace modifiers |
| COMPONENTS.md | Shell components, icons, images, skeleton, fonts, safe area, custom components |
| DIRECTIVES.md | wire:nb-bridge, wire:nb-navigate, nb-feedback, native actions |
| PLUGINS.md | Built-in Tauri 2 plugin bridges (dialogs, notifications, clipboard, geolocation, haptics, biometric, barcode, NFC, opener, OS info) |
| PUSH.md | Server push notifications via FCM (Android) and APNS (iOS) |
| MEDIA.md | Native camera, gallery and video pickers with on-device resize |
| ANIMATIONS.md | nb-animation, Animate.css, custom animations, haptic feedback |
| DATABASE.md | SQLite local, native MySQL/PostgreSQL/MariaDB via Rust bridge |
| CACHE.md | Cache::* auto-wired to SQLite, TTL/locks/remember, state vs cache decision |
| MULTI-LANGUAGE.md | Device-language-first locale resolution, shell + Laravel translation layers, setLanguage() runtime switching, accessibility |
| LIFECYCLE.md | Boot sequence, onBoot hook, clock sync, migrations |
| SCHEDULER.md | Task scheduling with Rust native timers |
| FILESYSTEM.md | Native filesystem, Storage driver, camera integration |
| BUILD.md | Build command, output, CLI commands, icon generation |
| UPDATES.md | Auto-update for desktop and mobile |
| PUBLISH.md | Publishing to stores |
| MCP.md | Built-in MCP server so Claude Code / Cursor / Windsurf can introspect your live project (declared plugins, facade methods, framework docs) |
| NativeBlade | Electron | React Native | Flutter | |
|---|---|---|---|---|
| Language | PHP + Blade | JavaScript | JavaScript | Dart |
| Backend | Built-in (Laravel) | Separate | Separate | Separate |
| Binary Size | ~15 MB | ~150 MB | ~30 MB | ~20 MB |
| App Bundle | ~6 MB gzip (full Laravel) | — | — | — |
| Learning Curve | None (if you know Laravel) | Medium | High | High |
| Native UI | Shell + WebView | WebView only | Native | Custom rendering |
| Offline | Yes (WASM + IndexedDB) | Manual | Manual | Manual |
NativeBlade ships with a three-layer test suite that runs on every push to main via GitHub Actions.
PHP — PHPUnit against Laravel 11/12/13 on PHP 8.3/8.4/8.5 via Testbench:
composer test
composer test:coverage # text summary (needs pcov or Xdebug)
composer test:coverage-html # full HTML report in build/coverage/JavaScript — node:test suite covering the runtime bridges (db/http/fs), action handlers and ctx helpers. No browser needed:
npm testRust — cargo test suite covering the Tauri command handlers (config, fileops, database row serialization, scheduler). sqlx integration tests use in-memory SQLite:
cd rust
cargo test --libRun everything locally:
composer test && npm test && (cd rust && cargo test --lib)NativeBlade is free and 100% open source, with no paid tier and no closed core. If it saves your team time, sponsoring is how you keep it that way.
- Sponsor the project — support tiers for individual developers and companies, plus sponsored features
- Business Support — a private channel and a guaranteed response time for teams running NativeBlade in production
See CONTRIBUTING.md for guidelines.
MIT
Built with Laravel, Livewire, Tauri, and PHP WebAssembly.
Jefferson T.S

