nativeblade/nativeblade

Build desktop and mobile apps with Laravel + Livewire

Maintainers

Package info

github.com/NativeBlade/NativeBlade

pkg:composer/nativeblade/nativeblade

Statistics

Installs: 61

Dependents: 1

Suggesters: 0

Stars: 100

Open Issues: 0


README

NativeBlade

Build desktop & mobile apps with Laravel + Livewire. No Electron. No React Native. Just PHP.

Tests Discord

ConfigurationComponentsDirectives & AttributesPluginsPushAnimationsLifecycleDatabaseFilesystemBuildSchedulerAuto-UpdatePublish

NativeBlade Demo

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.

Features

  • 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
  • Animations — 90+ Animate.css animations + custom NativeBlade animations via nb-animation attribute
  • Offline-First — SQLite persisted to IndexedDB, works without a server
  • Hot Reload — Vite HMR for instant feedback during development, plus --build mode 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

Requirements

  • PHP 8.3, 8.4, or 8.5 (with GD extension)
  • Laravel 11, 12, or 13
  • Livewire 3
  • Node.js 20+
  • Rust — install here

Quick Start

# 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.

Add Mobile

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.10

Mobile requires --host=<your-local-ip> so the device/emulator can reach the Vite dev server running on your machine. Replace 192.168.0.10 with your computer's LAN IP (find it with ipconfig on Windows or ifconfig / ip addr on macOS/Linux). localhost won't work because the phone can't reach your machine through it.

Preview the Production Bundle

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 --build

Builds 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.

How It Works

┌─────────────────────────────────────────────┐
│  Tauri Shell (native window)                │
│  ┌──────────────────────────────┐           │
│  │  Top Bar / Header            │  ← Shell  │
│  ├──────────────────────────────┤           │
│  │                              │           │
│  │   iframe (srcdoc)            │           │
│  │   ┌──────────────────────┐   │           │
│  │   │  Laravel + Livewire  │   │  ← Your  │
│  │   │  via PHP WebAssembly │   │    App    │
│  │   └──────────────────────┘   │           │
│  │                              │           │
│  ├──────────────────────────────┤           │
│  │  Bottom Navigation           │  ← Shell  │
│  └──────────────────────────────┘           │
└─────────────────────────────────────────────┘
  1. Boot — PHP WebAssembly loads your Laravel app (8.3, 8.4, or 8.5)
  2. Migrate — Pending migrations run automatically
  3. onBoot — Your startup code runs (license check, data sync, API calls) while splash is visible
  4. Route — Each navigation runs through Laravel's router inside WASM
  5. Render — Blade/Livewire HTML is rendered in an iframe
  6. Intercept — Fetch interceptor routes HTTP requests through WASM
  7. Bridge — Native actions flow through postMessage
  8. Schedule — Rust timers execute recurring tasks via Laravel Schedule
  9. Persist — SQLite syncs to IndexedDB automatically

Database

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]);

onBoot Hook

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.

Scheduler

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.

State Management

use NativeBlade\Facades\NativeBlade;

NativeBlade::setState('auth.user', ['name' => 'John']);
$user = NativeBlade::getState('auth.user');
NativeBlade::forget('auth.user');
NativeBlade::flush();

Log

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.

Platform Detection

NativeBlade::platform();   // 'windows', 'macos', 'linux', 'android', 'ios'
NativeBlade::isDesktop();
NativeBlade::isMobile();
NativeBlade::isAndroid();
NativeBlade::isIos();

Navigation

NativeBlade::navigate('/dashboard')->toResponse();
NativeBlade::navigate('/', replace: true)->toResponse();
<button wire:nb-navigate="/users">Users</button>
<button wire:nb-navigate.replace="/">Home</button>

Authentication

// 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();

HTTP Bridge

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'),
]);

Laravel Compatibility

Works Via Bridge Not Available
Routing, Blade, Livewire Http facade Queues, Mail (SMTP)
Eloquent (SQLite) External APIs Redis, Memcached
Middleware, Validation Native Filesystem WebSockets
Collections, Carbon MySQL, Postgres
Service Container Artisan CLI
Localization File Storage (S3)
Task Scheduling (via Rust)
Migrations (auto on boot)

Documentation

Doc Description
CONFIGURATION.md Desktop, Android, iOS configs, permissions, privacy manifest, transitions
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)
ANIMATIONS.md nb-animation, Animate.css, custom animations, haptic feedback
DATABASE.md SQLite local, native MySQL/PostgreSQL/MariaDB via Rust bridge
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

How NativeBlade Differs

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

Testing

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/

JavaScriptnode:test suite covering the runtime bridges (db/http/fs), action handlers and ctx helpers. No browser needed:

npm test

Rustcargo test suite covering the Tauri command handlers (config, fileops, database row serialization, scheduler). sqlx integration tests use in-memory SQLite:

cd rust
cargo test --lib

Run everything locally:

composer test && npm test && (cd rust && cargo test --lib)

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT

Built with Laravel, Livewire, Tauri, and PHP WebAssembly.
Jefferson T.S