Hello everyone 👋,
There is no doubt that reusable components is one of the main keys that makes developers life easier. Not only it makes the code elegant and sexy 🌶️, but also it can be used in multiple projects and with just few tweaks and Voilà! You have another version of your component effortlessly. In this article, I'm going to help you creating Vue JS 3 drop-down component with composition API styled Tailwind CSS and imported in Laravel 9 project. Our component is going to be reusable, dynamic and animated as well.
Take a peak on what we will be creating 👇 :
I'm sure you can style it better than this, I just wanted to show you variety of options.
Another peak on the code 👇 :
<Dropdown title="Users Dropdown">
<Dropdowncontent>
@foreach ($users as $user)
<Dropdownitems item="{{ $user->name }}"
href="/users/{{ $user->id }}">
</Dropdownitems>
@endforeach
</Dropdowncontent>
</Dropdown>
Before we start, please raise your hand if you have any questions:
🙋♂️ You keep mentioning 'reusable', what does it mean?
- A reusable component is a component that can be used in various parts of an application. For example, once we build our drop-down today you can use it in your web app as many times as you want without having to duplicate your code.
1- Prerequisites:
- I'm expecting that you have very basic knowledge of Vue composition API, Tailwind CSS and Laravel. If you don't, just come along for the ride and follow each step carefully and I guarantee you'll be able to do it by yourself by the time you finish this one.
- I will build the reusable component from scratch continuing on using the same REPO that we created in the previous article.
- If you would like to code along without my repo just make sure to install Laravel, Tailwind and Vue Js 3. Also create a database with users table or whatever and connect to it as we will be looping over the table. Instructions on how to do so is in PART 1.
- Make sure you're running the command
npm run watch
all the time.
2- Allow Tailwind to read Vue files:
- Head to tailwind.config.js and within the content array, be specific about your Vue files path.
module.exports = {
content: [
"./resources/**/*.blade.php",
"./resources/**/*.js",
"./resources/**/*.vue",
"./resources/js/components/*.vue",
"./resources/js/**/*.vue",
],
theme: {
extend: {},
},
plugins: [],
};
- Insure
npm run watch
is running in the terminal.
3- Create a general Dropdown.vue component:
- Head to
resources/js/components
and create new file calledDropdown.vue
. Within this file just build a normal drop-down component without looping over any data in database, just a toggle with
showUsers
function and some tailwind.Dropdown.vue
:
<template>
<div v-click-outside="onClickOutside" @click="showUsers">
<!-- toggler -->
<button
class="flex max-h-52 w-full overflow-auto py-2 pl-3 pr-9
text-sm font-semibold lg:inline-flex lg:w-48">
Users
</button>
<!-- Content Container -->
<div v-show="usersToggle" class="mt-2 w-full rounded-lg bg-
blue-100 py-2">
<!-- Drop-down Items -->
<a href="#" class="block text-sm my-1 mx-1">
Random user name
</a>
</div>
</div>
</template>
<script>
import vClickOutside from "click-outside-vue3";
import { ref } from "vue";
export default {
name: "Dropdown",
directives: {
clickOutside: vClickOutside.directive,
},
setup() {
const usersToggle = ref(false);
const showUsers = () => {
usersToggle.value = !usersToggle.value;
};
const onClickOutside = (event) => {
usersToggle.value = false;
};
return {
usersToggle,
showUsers,
onClickOutside,
};
},
};
</script>
- Import your component in
resources/js/app.js
.
require("./bootstrap");
import { createApp } from "vue";
import Dropdown from "./components/Dropdown";
import vClickOutside from "click-outside-vue3";
createApp({
components: {
Dropdown,
},
})
.use(vClickOutside)
.mount("#app");
Just a reminder that the vClickOutside is an npm package we used in the previous tutorial to close the drop-down when we click anywhere on the screen.
With nothing else, if you run the command
php artisan serve
in your Laravel's main directory you will see a functional ugly drop-down 🍾 But don't worry, we will make it prettier later.
4- Create component parts:
Now we're going to slice our component as we need 3 parts. One for the toggler, one for the content container and one for the drop-down items. But first we must create and import them.
- Head to
resources/js/components/
and create the other two filesDropdowncontent.vue
andDropdownitems.vue
. - Just make them contain
<template>
and<script>
tags and leave them empty for now. Import them in
resources/js/app.js
like we did with the first one.resources/js/app.js
:
import { createApp } from "vue";
import Dropdown from "./components/Dropdown";
import Dropdowncontent from "./components/Dropdowncontent";
import Dropdownitems from "./components/Dropdownitems";
import vClickOutside from "click-outside-vue3";
createApp({
components: {
Dropdown,
Dropdowncontent,
Dropdownitems,
},
})
.use(vClickOutside)
.mount("#app");
5- Slice Dropdown.vue
:
- Head to
resources/js/components/Dropdown.vue
and copy the div the contains the link and leave in it's place<slot />
tag and wrap the botton with<slot name="toggler"></slot>
tags. - Head to
resources/js/components/Dropdowncontent.vue
and paste the div in the template. Then copy the link inside the div from Dropdowncontent.vue to Dropdownitems.vue and leave in it's place<slot />
tag as well. Your components should look like this:
Dropdown.vue
:
<template>
<div v-click-outside="onClickOutside" @click="showUsers">
<!-- toggler -->
<slot name="toggler">
<button
class="flex max-h-52 w-full overflow-auto py-2 pl-3 pr-9
text-sm font-semibold lg:inline-flex lg:w-48">
Users
</button>
</slot>
<!-- Content Container -->
<slot />
</div>
</template>
-
Dropdowncontent.vue
:
<template>
<div v-show="usersToggle" class="mt-1 w-full rounded-xl bg-blue-
100 py-2">
<slot />
</div>
</template>
<script>
export default {
name: "Dropdowncontent",
};
</script>
-
Dropdownitems.vue
:
<template>
<a href="#" class="block text-sm my-1 mx-1"> A user name </a>
</template>
<script>
export default {
name: "Dropdownitems",
};
</script>
Head to
welcome.blade.php
and let's break it down in the body.welcome.blade.php
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>dropdown</title>
<link href="{{ asset('css/app.css') }}" rel="stylesheet" />
<script src="{{ asset('js/app.js') }}" defer></script>
</head>
<body>
<main class="max-w-6xl mx-auto mt-6 lg:mt-20 space-y-6"
id="app">
<div class="max-w-xl mx-auto mt-10 text-center">
<div class="space-y-2 lg:space-y-0 lg:space-x-4 mt-8">
<div
class="relative lg:inline-flex items-center bg-blue-
100 rounded-xl">
<Dropdown>
<Dropdowncontent>
<Dropdownitems></Dropdownitems>
</Dropdowncontent>
</Dropdown>
</div>
</div>
</div>
</main>
</body>
</html>
- Now if you check it on browser, you will see the same thing but the toggler won't work. You might be wondering about how the heck are we going to pass
usersToggle
value fromDropdown.vue
toDropdowncontent.vue
and really it's pretty simply, we're going to use Vue's provide/inject.
6- Provide/Inject:
Head to Dropdown.vue and import provide from vue, use it in the setup.
Dropdown.vue
:
<script>
import vClickOutside from "click-outside-vue3";
import { provide, ref } from "vue";
export default {
name: "Dropdown",
directives: {
clickOutside: vClickOutside.directive,
},
setup() {
const usersToggle = ref(false);
const showUsers = () => {
usersToggle.value = !usersToggle.value;
};
const onClickOutside = (event) => {
usersToggle.value = false;
};
provide("usersToggle", usersToggle);
return {
usersToggle,
showUsers,
onClickOutside,
};
},
};
</script>
Head to Dropdowncontent.vue and import inject, use it and return the value.
Dropdowncontent.vue
:
<script>
import { inject } from "vue";
export default {
name: "Dropdowncontent",
setup() {
const usersToggle = inject("usersToggle");
return {
usersToggle,
};
},
};
</script>
- And just like that, our drop-down is working again.
7- Pass the data from Blade to Vue:
To make this component more reusable and dynamic, let's head to Dropdown.vue and instead of hard coding the word 'Users' leave instead
{{ title }}
and accept title as aprop
in the script.Dropdown.vue :
<template>
<div v-click-outside="onClickOutside" @click="showUsers">
<!-- toggler -->
<button
class="flex max-h-52 w-full overflow-auto py-2 pl-3 pr-9
text-sm font-semibold lg:inline-flex lg:w-48">
{{ title }}
</button>
<!-- Content Container -->
<slot />
</div>
</template>
<script>
import vClickOutside from "click-outside-vue3";
import { provide, ref } from "vue";
export default {
name: "Dropdown",
props: ["title"],
directives: {
clickOutside: vClickOutside.directive,
},
setup() {
const usersToggle = ref(false);
const showUsers = () => {
usersToggle.value = !usersToggle.value;
};
const onClickOutside = (event) => {
usersToggle.value = false;
};
provide("usersToggle", usersToggle);
return {
usersToggle,
showUsers,
onClickOutside,
};
},
};
</script>
Head to Dropdownitems.vue and do the same. Remove 'A user name' and leave instead
{{ itemName }}
and accept it as aprop
.Dropdownitems.vue
:
<template>
<a href="#" class="block text-sm my-1 mx-1"> {{ item }} </a>
</template>
<script>
export default {
name: "Dropdownitems",
props: ["item"],
};
</script>
Head to
welcome.blade.php
and pass on the values.welcome.blade.php
:
<Dropdown title="Users Dropdown">
<Dropdowncontent>
<Dropdownitems item="Testing Items" href="#" />
</Dropdowncontent>
</Dropdown>
- Use @foreach loop to loop over all users in database and echo out their names.
<Dropdown title="Users Dropdown">
<Dropdowncontent>
@foreach ($users as $user)
<Dropdownitems item="{{ $user->name }}"
href="/users/{{ $user->id }}">
</Dropdownitems>
@endforeach
</Dropdowncontent>
</Dropdown>
And there you go 🚀 This is a complete dynamic and reusable drop-down component. You can even add to classes by passing them from blade just like we did with title, item name and href.
8- Adding animation and styles:
- You can get SVG website and add it to the button in
Dropdown.vue
. - I added a class conditionally for it to rotate when the
usersToggle
is true. Also i made few changes to Tailwind CSS styles.
Dropdown.vue
:
<template>
<div class="relative" v-click-outside="onClickOutside" @click="showUsers">
<slot name="toggler">
<button
class="text-white bg-gradient-to-r from-purple-600 to-
blue-500 rounded-xl flex max-h-52 w-full overflow-
auto py-2 pl-3 pr-9 text-sm font-semibold
lg:inline-flex lg:w-52">
{{ title }}
<svg
class="absolute"
:class="usersToggle
? '-rotate-90 transform transition duration-500 ease-in-out'
: 'rotate-90 transform transition duration-500 ease-in-out'"
style="right: 12px"
width="22"
height="22"
viewBox="0 0 22 22">
<g fill="none" fill-rule="evenodd">
<path
stroke="#000"
stroke-opacity=".012"
stroke-width=".5"
d="M21 1v20.16H.84V1z"
></path>
<path
fill="#222"
d="M13.854 7.224l-3.847 3.856 3.847 3.856-1.184 1.184-5.04-5.04 5.04-5.04z"
></path>
</g>
</svg>
</button>
</slot>
<slot />
</div>
</template>
I used Vue's
<transition>
tags combined with tailwind CSS classes to make a smooth effect for the drop-down.Dropdwoncontent.vue
:
<template>
<transition
:duration="1000"
enter-active-class="transform transition duration-300 ease-
custom"
enter-class="-translate-y-1/2 scale-y-0 opacity-0"
enter-to-class="translate-y-0 scale-y-100 opacity-100"
leave-active-class="transform transition duration-300 ease-
custom"
leave-class="translate-y-0 scale-y-100 opacity-100"
leave-to-class="-translate-y-1/2 scale-y-0 opacity-0">
<div
v-show="usersToggle"
class="absolute left-0 right-0 z-50 mt-2 w-full rounded-lg
bg-gradient-to-r from-purple-600 to-blue-500 py-1 px-
3 text-left text-sm font-semibold text-white
transition duration-300 py-2">
<slot />
</div>
</transition>
</template>
- Dropdownitems.vue :
<template>
<a
href=""
class="px-1 py-1 my-1 rounded-lg block hover:bg-gradient-to-bl
focus:bg-gradient-to-bl focus:ring-4 focus:ring-blue-
300 dark:focus:ring-blue-800">
<slot>{{ item }}</slot>
</a>
</template>
9- Extracting UsersDropdown blade component:
- This is not necessary as the code still looks great but we can extract a blade component for it and.
- Create new folder named
components
inresources/views
and create inside it a new file namedusers-dropdown.blade.php
. - Cut your drop-down code and paste it inside, leave in it's place
<x-users-dropdown />
. - That will leave us with only this code in our welcome.blade.php:
<!-- Only this -->
<x-users-dropdown />
Congratulations 🥳 Now you have a Vue animated, dynamic, reusable and colorful drop-down component inside a blade component with a very elegant code and cool Tailwind styles.
That's it for this tutorial and I hope you enjoyed reading it as I made huge effort due to the article being deleted and I had to rewrite it again. If you liked it please leave a reaction and if you have any questions leave it below or send me a DM on TWITTER directly.
Have a great day and happy coding 👋
Top comments (2)
Nice!
Thanks 🙏