Turbolinks

Turbolinks provides out-of-box support to convert simple web application into a Single Page Application(SPA).

Get the performance benefits of a single-page application without the added complexity of a client-side JavaScript framework, as stated in their github repo.

Some of the features it provides are given below:

  • Ability in navigating to any part of the web app without reloading the entire page.
  • Increase in performance.
  • SEO friendly.
  • Easy client-side integration.

To make Third Party Libraries work alongside Turbolinks, we have to initialise Turbolinks first.

document.addEventListener('turbolinks:load', function() {
   // third party init goes here
});

You need to add the data-turbolinks-track="true" attribute to any scripts that load on a per-page basis.

<script 
    defer src="{{ mix('js/posts.js') }}" 
    data-turbolinks-track="true"
></script>

<link 
    href="{{mix('css/app.css')}}" 
    rel="stylesheet" 
    data-turbolinks-track="true"
>

To completely disable caching in your application, ensure every page contains a no-cache directive.

<head>
    ...
    <meta name="turbolinks-cache-control" content="no-cache">
</head>

Persisting Elements Across Page Loads

<div id="cart-counter" data-turbolinks-permanent>1 item</div>

Integration with Laravel

Create a middleware to handle the redirect issue of Turbolinks with Laravel.

<?php

namespace App\Http\Middleware;

use Closure;

class SetTurbolinksHeader
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        // if($request->has('page')) {
        //     return $response;
        // } else {
        //     $response->header('Turbolinks-Location', $request->url());
        // }

        $turbolinksLocation = session('_turbolinks_location');

        if ($request->ajax() && ! $request->isMethod('get')) {
            $script = [];
            $script[] = "Turbolinks.clearCache()";
            $script[] = "Turbolinks.visit('" . $turbolinksLocation . "')";

            $response->setContent(join(";", $script));
            $response->header('Content-Type', 'application/javascript');
            $response->setStatusCode(202);
        } else if ($turbolinksLocation) {
            $response->header("Turbolinks-Location", $turbolinksLocation);
        }

        return $response;
    }
}

Put it inside the Kernel.php.

protected $middlewareGroups => [
    'web' => [
        // Other Middlewares

        App\Http\Middleware\SetTurbolinksHeader::class,
    ]
];  

Turbolinks Macro For Redirect in Controller

Create a new service provider

php artisan make:provider ResponseTurbolinksServiceProvider
<?php

namespace App\Providers;

use Response;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Routing\ResponseFactory;

class ResponseTurbolinksServiceProvider extends ServiceProvider
{
    public function boot(ResponseFactory $factory)
    {
        Response::macro('turbolinks', function ($url) use ($factory) {
            return $factory->redirectTo($url)
                ->with('_turbolinks_location', $url);
        });
    }

    public function register()
    {
    }
}

Register your new service provider in config/app.php.

'providers' => [
    // Other Service Providers

    /*
     * Turbolinks Service Providers...
     */
    App\Providers\ResponseTurbolinksServiceProvider::class,
]   

Example Controller

<?php

namespace App\Http\Controllers;

use App\Http\Requests;
use App\Http\Requests\StoreRequest;
use Illuminate\Http\Request;

class HomeController extends Controller
{
    public function index()
    {
        return view('home');
    }

    public function example()
    {
        return response()->turbolinks('/home');
    }

    public function login()
    {
        return view('login');
    }

    public function store(StoreRequest $request)
    {
        return response()->turbolinks('/home');
    }

    /**
     * Alternative
     *
     * Using Validation
     */
    public function storeWithManualValidation(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'email' => 'required',
            'password' => 'required'
        ]);

        if ($validator->fails()) {
            return back()
                ->withErrors($validator)
                ->withInput();
        }

        return redirect('/home')
            ->with('_turbolinks_location', '/home');
    }
}

Display Progress Bar

Turbolinks installs a CSS-based progress bar to provide feedback while issuing a request. The progress bar is enabled by default.

.turbolinks-progress-bar {
    height: 5px;
    background-color: green;
}

It appears automatically for any page that takes longer than 500ms to load.

References