Invoice Ninja – Laravel-powered solution for better invoicing

We continue the series of reviews for Laravel-based projects and apps. Today we have Invoice Ninja – a really powerful tool for, you guessed it, invoicing. It really impressed me, let me tell you how.

Other reviews in this series:

  1. FlarePoint: Laravel-based free CRM
  2. Invoice Ninja – Laravel-powered solution for better invoicing
  3. Attendize – event tickets selling system based on Laravel 5
  4. Ribbbon – project management system on Laravel 5.1 and Vue.js
  5. Faveo: impressive helpdesk system built on Laravel
  6. Confomo: Laravel-based website to meet Twitter friends at conferences

First things first – Invoice Ninja has a really interesting business model. They have two different versions – hosted and self-hosted. In other words, you can register at and use their online version, paying for premium services, or you can download the code for free (Laravel 5.2) and install it on your server for free.

It reminds me of another tool that has hosted and self-hosted open-source version – it’s analytics app called Piwik.

Anyway, we will be talking about self-hosted version here – we’re more interested in Laravel code here. So let’s begin.


Interestingly, though the code is available on Github, in their official documentation in Readme they recommend not to clone repository code, but to download ZIP file. And it’s actually quite heavy – 91 MB as ZIP and 247 MB unpacked.

invoice ninja archive

Another interesting thing is that ZIP archive comes with already included vendor folder in it, so after un-archiving you don’t need to run composer install. Instead, when you launch your homepage, you get a CMS-like installation page and you fill in your credentials.

invoice ninja install

Installation process went well, although I was waiting for it to finish quite long – around 1 minute my Chrome tab looked like this:


But after the process finished, I’ve got a clean and nice login screen.

invoice ninja login screen

All in all, I like the installation and information is prepared well-enough to install self-hosted version pretty smoothly.


Ok, let’s go on and log in. After logging in, I got into a dashboard. Empty, of course, but quite impressive.

invoice ninja dashboard

Ok, so what should I do now – probably, create a new client first? Here’s how it looks:

invoice ninja create new client

From this screen, and also from the amount of menu items on the left, I got an impression how well-thought the process actually is. Creators thought of the case that client might have more than one contact person. Looking further, I’ve noticed that such details are all over the Invoice Ninja. Well done, guys.

After creating a client, what do we do next? Of course, create an invoice for that client.

invoice ninja new invoice

The form is pretty simple, but at the same time might be quite complicated – depending on what you actually need: to issue a simple invoice or to customize it quite a bit.

What I loved that while I am filling in the details, on the bottom there is a “preview” mode which is refreshed on every change of any field. Impressive.

invoice ninja preview

You can also choose a design theme for your invoices. In online hosted version, this is one of the “premium” features – choose more complex invoice design and add your own logo there.

invoice ninja choose theme

To be honest, I didn’t dig deeper into features, but first impression is really really solid. You can feel that Invoice Ninja has been on the market already for a couple years, and it’s a pretty polished and serious product – quite a competitor to other more expensive enterprise invoicing tools out there.

Laravel code

Ok, and finally – what’s under the hood? How good/complex is Laravel code, is it easy to customize? I usually start judging the quality from routes file – in this case it’s Laravel 5.2 so it’s app/Http/routes.php. And it’s actually HUGE:

// Application setup
Route::get('/setup', 'AppController@showSetup');
Route::post('/setup', 'AppController@doSetup');
Route::get('/install', 'AppController@install');
Route::get('/update', 'AppController@update');

// Public pages
Route::get('/', 'HomeController@showIndex');
Route::get('/terms', 'HomeController@showTerms');
Route::get('/log_error', 'HomeController@logError');
Route::get('/invoice_now', 'HomeController@invoiceNow');
Route::get('/keep_alive', 'HomeController@keepAlive');
Route::post('/get_started', 'AccountController@getStarted');

// Client visible pages
Route::group(['middleware' => 'auth:client'], function() {
    Route::get('view/{invitation_key}', 'ClientPortalController@view');
    Route::get('download/{invitation_key}', 'ClientPortalController@download');
    Route::get('view', 'HomeController@viewLogo');
    Route::get('approve/{invitation_key}', 'QuoteController@approve');
// ...

Total 350 lines of code in routes. And I believe it’s not really too much – the application is that big, seriously.

What surprised me there is that in routes file there are constants defined at the end:

if (!defined('CONTACT_EMAIL')) {
    define('CONTACT_EMAIL', Config::get('mail.from.address'));
    define('CONTACT_NAME', Config::get(''));
    define('SITE_URL', Config::get('app.url'));

    define('ENV_DEVELOPMENT', 'local');
    define('ENV_STAGING', 'staging');

    define('RECENTLY_VIEWED', 'recent_history');
// ...

In fact, another 500 lines of code for constants. I probably wouldn’t put it in routes file, but as long as it works… there might have been some logic in that.

Also at the very end routes contains a few functions. Yes, functions, just like that. Go figure.

    function uctrans($text)
        return ucwords(trans($text));

    // optional trans: only return the string if it's translated
    function otrans($text)
        $locale = Session::get(SESSION_LOCALE);

        if ($locale == 'en') {
            return trans($text);
        } else {
            $string = trans($text);
            $english = trans($text, [], 'en');
            return $string != $english ? $string : '';

Now, let’s take a look at one of the controllers – for example, ClientController.php:

    protected $clientService;
    protected $clientRepo;
    protected $entityType = ENTITY_CLIENT;

    public function __construct(ClientRepository $clientRepo, ClientService $clientService)

        $this->clientRepo = $clientRepo;
        $this->clientService = $clientService;

     * Display a listing of the resource.
     * @return Response
    public function index()
        return View::make('list', [
            'entityType' => ENTITY_CLIENT,
            'title' => trans('texts.clients'),
            'sortCol' => '4',
            'columns' => Utils::trans([

Services and Repositories. Good. So should be a good flexible architecture underneath.

View::make() syntax looks sooo Laravel 4, but it’s pretty understandable because Invoice Ninja is a couple years old, so I guess the upgrades to newer Laravel versions ended up pretty “hacky”.

Also there’s one list.blade.php for everything – might sound convenient, depends on a developer style, probably.

All in all, the code seems a little questionable – although it works well and should be easy to maintain and customize.

Also, I can say kind words about their creators involvement in the community and being open – they have open Trello board with roadmap, and also discuss their plans on Github.

So, in conclusion, if you want to trust your invoices for a tool based on Laravel, Invoice Ninja is a perfect and solid choice.

Other reviews in this series:

  1. FlarePoint: Laravel-based free CRM
  2. Invoice Ninja – Laravel-powered solution for better invoicing
  3. Attendize – event tickets selling system based on Laravel 5
  4. Ribbbon – project management system on Laravel 5.1 and Vue.js
  5. Faveo: impressive helpdesk system built on Laravel
  6. Confomo: Laravel-based website to meet Twitter friends at conferences

Like our articles?
Check out our Laravel online courses!


  1. Thanks for the excellent review! I’m the developer of the app, I thought it may be helpful to clarify a few points.

    – We created the self host zip to make the app easier to setup. Many of our users aren’t familiar with composer so we include all of the third party PHP libraries so they can just copy over the files.

    – We’ve been working on the app for over three years now. As you noticed the app still includes some Laravel 4 style code (the version we started with). As we update sections of the app we try to update the code but I imagine there will always be some older references.

    – This also explains why the setup took a while to complete, there are close to 90 migration files which can take some time to process.

    – We’re always trying to balance our time between adding new features and refactoring the code. There are many parts of the app I’d like to clean up but the core architecture has held up reasonably well. The one thing we got right here was the heavy use of events (inspired by some excellent Laracast videos).


Please enter your comment!
Please enter your name here