NashTech Blog

Table of Contents

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

Picture of Hieu Nguyen Dang

Hieu Nguyen Dang

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

Scroll to Top