Toggle Password Visibility inside Laravel Blade with Vue.js — inline-template
As a full-stack engineer, I’ve worked on several apps built on Laravel backend and Vue.js frontend, and sometimes it can be a bit of a hassle trying to get some simple things in place at the intersection of both stack ends. Luckily for us, Vue.js provides a sleek way to have the best of both worlds.
The Challenge
I have some logic like validation error/feedback handling being handled in Laravel’s templating engine — blade, but I also want to be able to easily add a reactive password visibility toggler with Vue.js, without needing to rewrite my blade error rendering codes, especially since I cannot use blade-specific syntaxes within a Vue.js component’s template. The Vue.js inline-template component attribute is built-in to help with these types of cases.
Take a look at an example component utilizing inline-template attribute:
// login.blade.php
...<div class="form-group">
<label for="password" class="form-label">{{ __('Password') }}</label>
<password-visibility inline-template>
<div class="input-group">
<input aria-label="Password" aria-describedby="password-addon" ref="password" id="password" type="password"
class="form-control @error('password') is-invalid @enderror" name="password"
required autocomplete="new-password" placeholder="********">
<span :title="!visible ? 'show password?' : 'hide password?'" @click="toggleVisibility"
class="input-group-text" id="password-addon"
style="border-left: none; border-top-left-radius: 0; border-bottom-left-radius: 0;">
<i class="fe" :class="[!visible ? 'fe-eye' : 'fe-eye-off']"></i>
</span>
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</password-visibility>
</div>...
In the above snippet, by adding inline-template attribute to the <password-visibility>…</password-visibility> element, we’re able to combine the characteristic of both a traditional blade template and a reactive Vue.js component template without any issues. Blade handles its designated error-related logic and lets Vue.js conditionally manipulate the necessary attributes accessible from the component JS module; updating elements like the eye icon, ‘show password’ text, title attribute, and the password input element’s type attribute, reactively.
See a screenshot below for clearer formatting:

// PasswordVisibility.vue
Since our template was written in-line, we ended up with a slimmer than usual component:
<script>
export default {
data() {
return {
visible: false
}
},
methods: {
toggleVisibility() {
const input = this.$refs.password;
if (input.type === 'password') {
input.type = 'text';
this.visible = true;
} else if (input.type === 'text') {
input.type = 'password';
this.visible = false;
}
}
}
}
</script>
Nothing too complicated here — A data property that defaults to false and can be conditionally updated, and a method that conditionally sets the value of our input element’s type attribute.
Wiring up both ends
This bit may depend on how your project is set up. For me, I have a passwordVisibility.js file in my resources > js directory that looks like this:
import Vue from "vue";
import PasswordVisibility from '../components/PasswordVisibility';
new Vue({
el: 'password-visibility',
components: {
PasswordVisibility
}
});
Using Webpack, all the necessary modules are bundled up and published to the specified public directories, alongside other specified assets.
// webpack.mix.js
const mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('resources/js/app.js', 'public/js')
.vue()
.js('resources/js/scripts/passwordVisibility.js', 'public/js/scripts')
.sass('resources/sass/app.scss', 'public/css');
As highlighted above, a compiled version of passwordVisibility.js will be published to the ‘public/js/scripts’, where we can easily access and include it in our project, like so:
// below page @ login.blade.php
@section('scripts')
<script src="/js/scripts/passwordVisibility.js"></script>
@endsection
And assuming everything is wired up correctly, we should see things working as expected in the browser.



Thanks for coming along, guys!