Back to articlesArticle

NativePHP: How to Build iOS and Android Apps with PHP

My read on what NativePHP actually buys a team, where it compresses the stack intelligently and where I would start distrusting the proposition.

9 min
0
build com carlos style anime illustration: the PHP elephant mascot in the center, flanked by the Android robot and the iOS logo, linked by a NativePHP bridge, with two phones running the same app.

Building iOS and Android apps with PHP? Understanding NativePHP

Core thesis

NativePHP is interesting not because it redefines mobile, but because it shortens the distance between Laravel teams, app distribution and product validation. The main point isn’t to replace Swift, Kotlin, Flutter or React Native, but to understand in which contexts reusing the stack pays for the tradeoffs in runtime, UI and ecosystem.

It’s worth separating two things people tend to conflate. The marketing promise is “write PHP, ship a native app”. The technical reality is more specific: you bundle a PHP interpreter inside a native shell, run your Laravel application locally on the device, and render the interface inside a WebView. Understanding that difference is what separates an architecture decision from a blind bet. The rest of this article is about exactly that: what happens inside the binary, where it pays off, and where it charges you.

Who this article is for

  • Technical leaders evaluating stacks for MVPs or internal tools
  • Laravel teams curious about NativePHP
  • Mobile engineers interested in mapping the real limits of the proposition

If you already run a mature native mobile team, you’ll read this as a map of “when a backend team should or shouldn’t step into your territory”. If you’re a Laravel team, read it as a reality check before you promise product an app.

Context

Mature backend teams frequently run into the same problem: they need to create an installable experience, but they don’t want to open a new mobile engineering front right at the start. Hiring iOS and Android engineers, standing up a build pipeline, learning the store rules and maintaining two native codebases is a large investment to validate a hypothesis that might not hold.

Historically, the answer to that dilemma has been the “web inside an app” family: Cordova, then Capacitor, and the PWA world. NativePHP sits in that same conceptual family, but with an important difference. Instead of moving the logic into client-side JavaScript, it embeds a PHP runtime and runs the entire Laravel application - routes, controllers, migrations, Eloquent - locally on the device. The project started on desktop (packaging Laravel apps as macOS, Windows and Linux executables) and the mobile variant applies the same idea to iOS and Android. For a team that already thinks in terms of request, response, model and view, this changes the mental model less than any other approach.

Simplified architecture

flowchart TD
  User["User"] --> Shell["Native shell (Swift / Kotlin)"]
  Shell --> Runtime["Embedded PHP Runtime"]
  Runtime --> Laravel["Laravel Application"]
  Laravel --> UI["HTML / CSS / JS UI"]
  Laravel --> Native["Bridge to native APIs"]
  Native --> Device["Camera, files, notifications"]

The right mental image is three layers stacked inside the same binary.

The first is the native shell. On iOS it’s written in Swift, on Android in Kotlin. That shell is the “real” app the store sees: it handles the lifecycle (launch, background, resume), hosts the WebView, and boots the PHP runtime when the app opens.

The second is the embedded PHP runtime. A PHP interpreter compiled for the device architecture (typically ARM64) ships inside the app bundle, along with your Laravel application code and the vendor directory of dependencies. When the app starts, the shell effectively brings up a local PHP server. No network is involved: requests go from the WebView to that server over localhost or a custom URL scheme, and come back.

The third is the bridge to native APIs. Since plain PHP can’t open the camera or fire a local notification, NativePHP exposes PHP facades that serialize a command and hand it to the native layer to execute. You call something in PHP, the shell translates it into a real Swift or Kotlin call, runs it, and returns the result.

The detail that makes this model work better than it sounds is locality. Because the “server” lives on the device itself, server-driven tooling like Livewire stops paying network latency. An interaction that in a traditional web app would be an internet round-trip becomes a millisecond localhost call. That’s what makes the UI feel reactive without writing heavy JavaScript.

How it works in practice

The workflow is deliberately familiar to anyone coming from Laravel:

composer require nativephp/mobile
php artisan native:install
php artisan native:serve
php artisan native:build

native:install prepares the native project (Xcode/Gradle) and the runtime integration. native:serve runs the app connected to your dev environment with hot reload, so editing a Blade view and seeing it on the device is immediate. native:build produces the distributable artifact (.ipa / .aab).

The app code stays plain Laravel:

Route::get('/', function () {
    return view('hello');
});
<!DOCTYPE html>
<html>
<head>
  <title>NativePHP Demo</title>
</head>
<body>
  <h1>Hello World</h1>
  <p>This app is running Laravel inside a mobile app.</p>
</body>
</html>

The difference shows up when you need hardware. Instead of writing Swift, you call a PHP facade that NativePHP maps to the native API. The exact shape varies by version, but the idea is always the same: a PHP method that triggers a device capability.

use Native\Mobile\Facades\Dialog;

Route::post('/confirm', function () {
    Dialog::alert('Saved', 'Your record was stored locally.');
    return back();
});

Local persistence follows the usual Laravel model: SQLite via Eloquent, migrations running on the device, all inside the app sandbox. That’s what makes real offline flows possible - the database is on the device, not behind a remote API. For cases that need to sync, the usual pattern is to treat the cloud backend as the source of truth and the local SQLite as a cache, the same design a well-built native app would adopt.

Where it creates value

  • Knowledge reuse by Laravel teams. A team that knows Eloquent, Blade, queues and the service container reuses almost everything. The learning curve stops being “a new platform” and becomes “a new deploy target”.
  • Speed gains on MVPs and internal apps. With no two parallel native codebases to maintain, a single codebase covers iOS and Android. To validate a product hypothesis, that shortens the path to having something in someone’s hands.
  • Lower initial friction to distribute installable apps. A lot of internal tooling today is a web panel nobody installs. Packaging it as an app solves home-screen presence, notifications and hardware access without rewriting the logic.
  • Real offline flows. With SQLite and the logic running on the device, the app works without connectivity by design, not as a feature bolted on later.

Where the tradeoffs bite

None of these is fatal on its own, but ignoring them is what creates technical debt early.

  • Binary size. You’re bundling a whole PHP interpreter, the Laravel framework and the entire vendor tree. That adds a fixed weight floor to the app before you write a single feature. For an internal utility, irrelevant; for a consumer app sensitive to install rates, it’s a cost to measure.
  • Slower startup. Cold start has to boot the PHP runtime and bootstrap Laravel (service providers, config, routes). Even locally, that isn’t instant. Config and route caching help, but it rarely matches the launch time of a lean native app.
  • Higher memory consumption. A live PHP runtime plus the WebView costs more RAM than pure native views. On entry-level devices, that pushes the app closer to the point where the system kills background processes.
  • UI less faithful to native. The interface is HTML/CSS rendered in a WebView. You can get far with a good web design system, but fine gestures, consistent 60fps transitions and that native platform “smell” are harder to nail.
  • Hardware access limited to what the bridge exposes. If a capability has no facade, you fall back to writing native code or a plugin - exactly the skill you were trying not to hire for.
  • Smaller, younger ecosystem. Fewer off-the-shelf plugins, fewer Stack Overflow answers, fewer documented production cases. That matters when something breaks the night before a launch.

Positioning comparison

Technology Language UI Expected performance Maturity
Native iOS / Android Swift / Kotlin Native Very high Very high
Flutter Dart Own rendering engine High High
React Native JS / TS Native bridge Medium to high High
Capacitor / Cordova JS / TS WebView Low to medium High
NativePHP PHP WebView + local runtime Low to medium Low

The table summarizes, but the boundaries deserve nuance.

Versus Flutter: Flutter compiles Dart to native code and draws the UI with its own engine (Skia/Impeller), delivering consistent animation and high visual fidelity. It’s the opposite of the WebView model. If pixels and frame rate matter, Flutter plays in another league.

Versus React Native: RN renders actual native views from JS. UI fidelity and scroll performance sit above any WebView approach. In exchange, you keep a JS/TS world and the bridge that comes with it.

Versus Capacitor / Cordova: this is the fairest comparison, because both use WebView + native bridge. The difference is where the logic runs. In Capacitor, the logic is client-side JavaScript and there’s no server. In NativePHP, there’s an actual local PHP server, so your domain logic runs in PHP on the device. For a Laravel team, that means reusing controllers, models and validation instead of rewriting them in JS.

Versus PWA: a PWA doesn’t go through the stores, ships no runtime, and has more restricted hardware access (though growing). NativePHP trades that lightness for store presence, a broader native bridge and robust local persistence.

When I would consider using it

  • Internal tools where experience matters less than having something installable fast
  • Administrative dashboards that are already web and just need to become an app
  • MVPs to validate a hypothesis before investing in dedicated mobile engineering
  • Products with a strong Laravel team, a short deadline and a need to learn fast from real users

The common thread is always the same: the cost of learning a new stack would outweigh the benefit, and UI experience isn’t the product’s competitive edge.

When I would avoid it

  • Core mobile product, where the app is the business and not an accessory to it
  • Highly refined UI, with animation, gestures and transitions as part of the value proposition
  • Apps with heavy hardware usage (real-time camera, high-frequency sensors, graphics)
  • Long roadmaps that depend on a mature library ecosystem and hiring pool
  • Cases that require frequent code updates through the store: App Store rules restrict downloading and executing new code at runtime, so NativePHP apps generally depend on the normal review cycle to ship logic changes, without the OTA shortcut a purely web app would have via its server

Conclusion

NativePHP can be a valid experiment track. I wouldn’t treat it as a natural replacement for mature mobile engineering. If the goal is stack compression and early speed, it’s worth a serious prototype: build a real flow, measure the binary size and cold-start time on the weakest device your audience carries, and test a native capability you know you’ll need. Those three numbers say more about viability than any marketing benchmark.

If the goal is experience, scale and platform longevity, I’d stick with more established stacks. The right question is never “can PHP become an app?” - it can. It’s “does this specific app, with this audience and this roadmap, fit inside the limits of a WebView with an embedded runtime without trapping me later?”.

Further reading

CTA

If this topic interests you, the discussion worth having is less about whether the idea is intriguing and more about where it actually fits without generating technical debt too early.