Make Reusable and Animated Drop-down with Vue, Tailwind and Laravel
Tags: laravel, vue, tailwindcss, tutorial
Published: 2022-02-12 15:23:43

## 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 👇 :

{% vimeo 676584884 %}

I'm sure you can style it better than this, I just wanted to show you variety of options.

Another peak on the code 👇 :

```php
<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.

|  Table of Content |
| ------------------- |
| [1- Prerequisites:](#chapter-1)            |
| [2- Allow Tailwind to read Vue files](#chapter-2)            |
| [3- Create a general Dropdown.vue component](#chapter-3)       |
| [4- Create component parts](#chapter-4)       |
| [5- Slice Dropdown.vue](#chapter-5)       |
| [6- Provide/Inject](#chapter-6)       |
| [7- Pass the data from Blade to Vue](#chapter-7)       |
| [8- Adding animation and styles](#chapter-8)       |
| [9- Extracting UsersDropdown blade component](#chapter-9)       

### 1- Prerequisites:<a name="chapter-1"></a>

- 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](https://github.com/MooseSaeed/dropdown-component) 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](https://dev.to/moose_said/create-dynamic-drop-down-component-with-vue-3-tailwind-css-and-laravel-9-2k4o).
- Make sure you're running the command `npm run watch` all the time.

### 2- Allow Tailwind to read Vue files:<a name="chapter-2"></a>

- Head to tailwind.config.js and within the content array, be specific about your Vue files path.

```js
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:<a name="chapter-3"></a>

- Head to `resources/js/components` and create new file called `Dropdown.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` :

```html
<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>
```

```javascript
<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`.

```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:<a name="chapter-4"></a>

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 files `Dropdowncontent.vue` and `Dropdownitems.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` :

```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`:<a name="chapter-5"></a>

- 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` :

```html
<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` :

```html
<template>
  <div v-show="usersToggle" class="mt-1 w-full rounded-xl bg-blue- 
                                   100 py-2">

    <slot />

  </div>
</template>
```

```javascript
<script>
export default {
    name: "Dropdowncontent",
};
</script>
```

- `Dropdownitems.vue` :

```html
<template>
  <a href="#" class="block text-sm my-1 mx-1"> A user name </a>
</template>
```

```javascript
<script>
export default {
    name: "Dropdownitems",
};
</script>
```

- Head to `welcome.blade.php` and let's break it down in the body.

- `welcome.blade.php` :

```html
<!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 from `Dropdown.vue` to `Dropdowncontent.vue` and really it's pretty simply, we're going to use Vue's provide/inject.

### 6- Provide/Inject:<a name="chapter-6"></a>

- Head to Dropdown.vue and import provide from vue, use it in the setup.

- `Dropdown.vue` :

```javascript
<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` :

```javascript
<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:<a name="chapter-7"></a>

- 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 a `prop` in the script.

- Dropdown.vue :

```html
<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>
```

```javascript
<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 a `prop`.

- `Dropdownitems.vue` :

```html
<template>
  <a href="#" class="block text-sm my-1 mx-1"> {{ item }} </a>
</template>
```

```javascript
<script>
export default {
    name: "Dropdownitems",
    props: ["item"],
};
</script>
```

- Head to `welcome.blade.php` and pass on the values.

- `welcome.blade.php` :

```html
<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.

```php
<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.

![Dynamic dropdown component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2bvmanfthvhytgvsygbo.gif)

### 8- Adding animation and styles:<a name="chapter-8"></a>

- You can get SVG [website](https://icons8.com/icons/set/dropdown) 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` :

```html
<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` :

```html
<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 :

```html
<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:<a name="chapter-9"></a>

- 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` in `resources/views` and create inside it a new file named `users-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:

```html

          <!-- 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**](https://twitter.com/Moose_Said) directly.

Have a great day and happy coding 👋