Adding Vue Devtools Support to Safari with Laravel

When developing applications with VueJS, the devtools are an excellent resource for debugging and development. For Chrome and Firefox, an official extension is available that adds support directly within each browser’s own devtools suite. Safari however, does not share the same extension model and therefor some workarounds are required to use the devtools with Apple’s browser.

In this tutorial, I’ll show you how to add support for Vue devtools in Safari within the context of a Laravel application.

Install Vue Devtools

First, you’ll need to install the Vue devtools locally. You can reference their documentation, but I recommend installing them globally with npm.

npm install -g vue-devtools

To start the devtools application and server, you can run vue-devtools in your terminal.

Note: You will need to run the `vue-devtools` command each time you want to start a debugging session.

Once the devtools are running, you should see a mostly blank screen like this.

The Vue Devtools Splash Screen

Custom Laravel Blade Directive

Here is where the magic happens. We’re going to add a custom blade directive that will inject the required script tag wherever we request it. A little logic will allow us to dynamically check the user agent and then output the script tag for the Safari browser.

<?php // within your AppServiceProvider::boot method.

Blade::directive('vuedevtools', function ($expression) {
	return <<<PHP
	<?php
		if (
			(
				app()->environment() === 'local' 
				&& ((bool) ! \Str::of(request()->headers->get('user-agent'))->contains('Chrome'))
			)
			|| env('USE_EXTERNAL_VUE_DEVTOOLS', false)
		) {
			echo '<script src="http://localhost:8098"></script>';
		}
	?>
	PHP;
});

Let’s go line by line and I’ll explain what is going on.

First, we register the directive with the static directive method on the Blade facade. Next we have a little trickery! Due to how template caching works, we need to evaluate these parameters within a PHP tag that is output in place of the directive.

To facilitate legibility, I’m using a heredoc to write the returned string.

Within the heredoc, we’ll first check if the app environment is equal to the string local. You can set this with the APP_ENV variable in your projects .env file. Next, using the Str facade and fluent strings, we will evaluate if the incoming request’s user agent includes the word Chrome. Due to historical norms, most web browser requests made on a Mac will include the word Safari somewhere in the User Agent. The Chrome user agent also includes its own name, so we can check for its existence. I haven’t tested this with Firefox, but if you’re seeing the extra script tag in that browser and you don’t want it, you should be able to clone this line and check for Firefox as well.

Finally, we’ll check for the existence of a USE_EXTERNAL_VUE_DEVTOOLS environment variable. Setting this to true will always render the script tag necessary for the standalone Vue devtools application to function, regardless of the browser being used.

If all of those variables evaluate to true, the app env is local, and we are not using Chrome, we’ll echo the script tag linking to the Vue devtools.

To make everything work, the last step is to include the @vuedevtools directive somewhere within your application’s main view file. This can be a Blade layout or component. Just know the script tag will be output in that location.

Conclusion

With this information, you should have data in Vue Devtools from your application. If you’d like to evaluate less code in your production environment, you can also wrap the directive in an environment check within your application’s layout file.

@env('local')
    @vuedevtools
@endenv

If you have any questions, feel free to ping me on Twitter!

Author: Daron

Professional software developer with extensive experience in Laravel and WordPress. When not writing code, he enjoys Netflix and playing Nintendo. He is also available for freelance opportunities. Get in touch to hash out the details.

Leave a Reply

Your email address will not be published.