Introduction
As a web developer, I’ve always been curious about building native desktop applications. When I discovered that I could leverage my existing Laravel knowledge to create desktop apps using NativePHP and Electron, I knew I had to try it. This is the story of how I built a money management desktop application and what I learned along the way.
Why Native Desktop Apps?
Before diving in, let me explain why I chose to build a desktop app instead of a web application:
Offline-first: Desktop apps work without an internet connection
Better performance: Native apps can leverage system resources more efficiently
System integration: Access to file system, notifications, and other OS features
User experience: Native feel and responsiveness that web apps sometimes lack
The Stack: Laravel + NativePHP + Electron
What is NativePHP?
NativePHP is a framework that allows you to build native desktop applications using PHP and Laravel. It wraps your Laravel application in an Electron shell, giving you the best of both worlds:
Familiar development: Use Laravel, Blade templates, and all your favorite packages
Native capabilities: Access to desktop features through Electron
Cross-platform: Build for Windows, macOS, and Linux from a single codebase
Why This Stack?
Laravel: I already knew Laravel well, so I could focus on building features rather than learning a new framework
Electron: Powers popular apps like VS Code, Slack, and Discord – proven technology
NativePHP: The bridge that makes it all work seamlessly together
Getting Started: Setting Up the Project
Step 1: Create a Laravel Project
composer create-project laravel/laravel money-manager
cd money-manager
Step 2: Install NativePHP
composer require nativephp/desktop
php artisan native:install --publish
This command:
Installs NativePHP and its dependencies
Sets up the Electron backend
Creates necessary configuration files
Installs NPM dependencies for Electron
Step 3: Configure the Main Window
NativePHP creates a NativeAppServiceProvider where you configure your app window:
public function boot(): void
{
Window::open()
->title('Money Manager')
->width(1200)
->height(800)
->route('dashboard');
}
Step 4: Run Your App
php artisan native:dev
Building the Money Manager App
Architecture Decisions
I decided to build a money management app because:
It’s practical and useful
It demonstrates CRUD operations
It requires data persistence (perfect for SQLite)
It showcases real-world desktop app patterns
Key Features Implemented
1. Account Management
Create multiple accounts (cash, bank, credit cards)
Track balances in different currencies
Edit and delete accounts
2. Transaction Tracking
Record income and expenses
Categorize transactions
Filter by date, account, category, or type
Automatic balance calculation
3. Dashboard & Analytics
Overview of total balance
Monthly income/expense tracking
Category breakdown
Recent transactions
4. Navigation System
Clean, organized navigation between sections
Separate pages for different features
Better UX than cramming everything on one page
Challenges and Solutions
Challenge 1: Version Format Error
Problem: Electron’s auto-updater requires valid semver version format.
Error: Error: App version is not a valid semver version: “v2”
Solution: Changed the version in nativephp/electron/package.json from “v2” to “2.0.0”.
{
"version": "2.0.0"
}
Challenge 2: Vite Manifest Not Found
Solution: Built the assets using:
npm run build
For development, you can run npm run dev in a separate terminal.
npm run dev
Challenge 3: Balance Recalculation
Problem: When editing or deleting transactions, account balances needed to be recalculated correctly.
Solution: Implemented a recalculateBalances() method that:
Gets all transactions before the affected date
Calculates the starting balance
Recalculates balances for all subsequent transactions
Updates the account’s current balance
private function recalculateBalances(Account $account, $fromDate)
{
// Get transactions before the date
$beforeTransactions = Transaction::where('account_id', $account->id)
->where('date', '<', $fromDate)
->orderBy('date')
->get();
$balance = $account->opening_balance;
foreach ($beforeTransactions as $tx) {
$balance += $tx->type === 'expense' ? -$tx->amount : $tx->amount;
}
// Recalculate from the affected date onwards
$transactions = Transaction::where('account_id', $account->id)
->where('date', '>=', $fromDate)
->orderBy('date')
->get();
foreach ($transactions as $tx) {
$balance += $tx->type === 'expense' ? -$tx->amount : $tx->amount;
$tx->balance_after = $balance;
$tx->save();
}
$account->current_balance = $balance;
$account->save();
}
Best Practices I Discovered
1. Keep It Simple
Start with a simple structure and add complexity as needed. Don’t over-engineer from the start.
2. Use Database Transactions
For financial operations, always use database transactions to ensure data integrity:
return DB::transaction(function () use ($data) {
// Your operations
});
3. Organize Your Code
Separate concerns:
Controllers handle logic
Models manage data relationships
Views focus on presentation
Routes define navigation
Conclusion
Building a native desktop app with Laravel and NativePHP was an eye-opening experience. It showed me that:
You don’t need to learn a completely new stack to build desktop apps
Laravel’s ecosystem works beautifully in desktop environments
NativePHP makes the transition from web to desktop seamless
Desktop apps can provide a better user experience for certain use cases
If you’re a Laravel developer curious about desktop apps, I highly recommend giving NativePHP a try. The learning curve is gentle, and you’ll be building native apps in no time!
Resources
- NativePHP Documentation – https://nativephp.com
- Laravel Documentation – https://laravel.com/docs
- Electron Documentation – https://www.electronjs.org/docs
- Project Repository – https://github.com/danghieu1407/ManageMoneyNativeApp.git