How to Create a Pricing Table with a Monthly/Yearly Toggle in Tailwind CSS and Vue
Tags: tailwindcss, vue, tutorial, webdev
Published: 2023-05-30 08:41:47

####**[Live Demo](https://cruip-tutorials-vue.vercel.app/pricing-table) / [Download](https://github.com/cruip/cruip-tutorials-vue/blob/main/src/components/PricingTable.vue)**

Welcome to the third and final part of our series on **How to Create a Pricing Table with a Monthly/Yearly Toggle Switch in Tailwind CSS**. As for the previous (and upcoming) series, our objective is to provide step-by-step instructions for building different **Tailwind CSS components** in HTML, React (with Next.js), and Vue. Our primary focus, however, is to show how to maintain code that is as similar and consistent as possible across these frameworks, especially Next.js and Vue.

In this final part, we will dive into creating the **pricing table component using Vue**, closely resembling what we previously demonstrated in the [Next.js tutorial](https://cruip.com/how-to-create-a-pricing-table-with-a-monthly-yearly-toggle-in-tailwind-css-and-next-js/). By crafting the Vue equivalent of the Next.js component, we aim to compare the two approaches, gain a comprehensive understanding of each framework’s logic, and elevate our development skills.

Let’s get started by creating a new file named `PricingTable.vue` within the components folder of our app. Copy the HTML code from the previous tutorial and proceed to complete it using Vue.

Then, define the structure of our Vue component:

```html
<script setup lang="ts">
</script>
<template>
    <div>
        <!-- Pricing toggle -->
        <!-- Pricing table -->
    </div>
</template>
```

Creating a variable to display annual or monthly pricing
--------------------------------------------------------

Just like we did previously with Next.js using `useState`, we will create a variable called `isAnnual` in Vue. This variable controls whether annual or monthly prices are displayed.

Let’s begin by creating this variable within the `<script>` tag using Vue’s `ref` function:

```html
<script setup lang="ts">
import { ref } from 'vue'
const isAnnual = ref<boolean>(true)
</script>
<template>
    <div>
        <!-- Pricing toggle -->
        <!-- Pricing table -->
    </div>
</template>
```

We are utilizing a `ref` because every time the user clicks on the buttons, we need to update the variable’s state, and subsequently update the component to display either annual or monthly prices.

The initial state of the `ref` is set to `true` since we want to show the annual prices by default.

Adding the pricing toggle
-------------------------

Let’s now create the toggle that allows users to switch between annual and monthly prices – as a reminder, the toggle consists of two button elements:

```html
<script setup lang="ts">
import { ref } from 'vue'
const isAnnual = ref<boolean>(true)
</script>
<template>
    <div>
        <!-- Pricing toggle -->
        <div class="flex justify-center max-w-[14rem] m-auto mb-8 lg:mb-16">
            <div class="relative flex w-full p-1 bg-white dark:bg-slate-900 rounded-full">
                <span class="absolute inset-0 m-1 pointer-events-none" aria-hidden="true">
                    <span class="absolute inset-0 w-1/2 bg-indigo-500 rounded-full shadow-sm shadow-indigo-950/10 transform transition-transform duration-150 ease-in-out" :class="isAnnual ? 'translate-x-0' : 'translate-x-full'"></span>
                </span>
                <button
                    class="relative flex-1 text-sm font-medium h-8 rounded-full focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150 ease-in-out"
                    :class="isAnnual ? 'text-white' : 'text-slate-500 dark:text-slate-400'"
                    @click="isAnnual = true"
                >Yearly <span
                    :class="isAnnual ? 'text-indigo-200' : 'text-slate-400 dark:text-slate-500'">-20%</span></button>
                <button
                    class="relative flex-1 text-sm font-medium h-8 rounded-full focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150 ease-in-out"
                    :class="isAnnual ? 'text-slate-500 dark:text-slate-400' : 'text-white'"
                    @click="isAnnual = false"
                >Monthly</button>
            </div>
        </div>        
        <!-- Pricing table -->
    </div>
</template>
```

As you may have noticed, we use the `:class` directive to dynamically manage CSS classes based on the value of the `isAnnual` variable.

When `isAnnual` is `true`, the element with the indigo background is positioned to the left, highlighting the “Yearly” button. Vice versa, when `isAnnual` is `false`, the element shifts to the right, highlighting the “Monthly” button.

Additionally, when `isAnnual` is `true`, the text of the first button appears white, while the text of the second button is gray. When `isAnnual` is `false`, the text of the first button appears gray, while the text of the second button is white.

Lastly, we add the `@click` directive to handle the click event on each button. Clicking the first button sets `isAnnual` to `true`, while clicking the second button sets it to `false`.

Creating the pricing table
--------------------------

With the toggle in place, we can now insert the pricing table. We will copy the HTML code from our previous tutorial, and complete it with Vue.

To dynamically handle the displayed prices based on the `isAnnual` variable, we’ll add the `v-text` directive. For example, in the first pricing tab:

    <span class="text-slate-900 dark:text-slate-200 font-bold text-4xl" v-text="isAnnual ? '29' : '35'"></span>

Copy

Here’s the complete code for the pricing table section:

```html
<div class="max-w-sm mx-auto grid gap-6 lg:grid-cols-3 items-start lg:max-w-none">
    <!-- Pricing tab 1 -->
    <div class="h-full">
        <div class="relative flex flex-col h-full p-6 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-900 shadow shadow-slate-950/5">
            <div class="mb-5">
                <div class="text-slate-900 dark:text-slate-200 font-semibold mb-1">Essential</div>
                <div class="inline-flex items-baseline mb-2">
                    <span class="text-slate-900 dark:text-slate-200 font-bold text-3xl">$</span>
                    <span class="text-slate-900 dark:text-slate-200 font-bold text-4xl" v-text="isAnnual ? '29' : '35'"></span>
                    <span class="text-slate-500 font-medium">/mo</span>
                </div>
                <div class="text-sm text-slate-500 mb-5">There are many variations available, but the majority have suffered.</div>
                <a class="w-full inline-flex justify-center whitespace-nowrap rounded-lg bg-indigo-500 px-3.5 py-2.5 text-sm font-medium text-white shadow-sm shadow-indigo-950/10 hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150" href="#0">
                    Purchase Plan
                </a>
            </div>
            <div class="text-slate-900 dark:text-slate-200 font-medium mb-3">Includes:</div>
            <ul class="text-slate-600 dark:text-slate-400 text-sm space-y-3 grow">
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Unlimited placeholder texts</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Consectetur adipiscing elit</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Excepteur sint occaecat cupidatat</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Officia deserunt mollit anim</span>
                </li>
            </ul>
        </div>
    </div>
    <!-- Pricing tab 2 -->
    <div class="h-full dark">
        <div class="relative flex flex-col h-full p-6 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-900 shadow shadow-slate-950/5">
            <div class="absolute top-0 right-0 mr-6 -mt-4">
                <div class="inline-flex items-center text-xs font-semibold py-1.5 px-3 bg-emerald-500 text-white rounded-full shadow-sm shadow-slate-950/5">Most Popular</div>
            </div>
            <div class="mb-5">
                <div class="text-slate-900 dark:text-slate-200 font-semibold mb-1">Perform</div>
                <div class="inline-flex items-baseline mb-2">
                    <span class="text-slate-900 dark:text-slate-200 font-bold text-3xl">$</span>
                    <span class="text-slate-900 dark:text-slate-200 font-bold text-4xl" v-text="isAnnual ? '49' : '55'"></span>
                    <span class="text-slate-500 font-medium">/mo</span>
                </div>
                <div class="text-sm text-slate-500 mb-5">There are many variations available, but the majority have suffered.</div>
                <a class="w-full inline-flex justify-center whitespace-nowrap rounded-lg bg-indigo-500 px-3.5 py-2.5 text-sm font-medium text-white shadow-sm shadow-indigo-950/10 hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150" href="#0">
                    Purchase Plan
                </a>
            </div>
            <div class="text-slate-900 dark:text-slate-200 font-medium mb-3">Includes:</div>
            <ul class="text-slate-600 dark:text-slate-400 text-sm space-y-3 grow">
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Unlimited placeholder texts</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Consectetur adipiscing elit</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Excepteur sint occaecat cupidatat</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Officia deserunt mollit anim</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Predefined chunks as necessary</span>
                </li>
            </ul>
        </div>
    </div>
    <!-- Pricing tab 3 -->
    <div class="h-full">
        <div class="relative flex flex-col h-full p-6 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-900 shadow shadow-slate-950/5">
            <div class="mb-5">
                <div class="text-slate-900 dark:text-slate-200 font-semibold mb-1">Enterprise</div>
                <div class="inline-flex items-baseline mb-2">
                    <span class="text-slate-900 dark:text-slate-200 font-bold text-3xl">$</span>
                    <span class="text-slate-900 dark:text-slate-200 font-bold text-4xl" v-text="isAnnual ? '79' : '85'"></span>
                    <span class="text-slate-500 font-medium">/mo</span>
                </div>
                <div class="text-sm text-slate-500 mb-5">There are many variations available, but the majority have suffered.</div>
                <a class="w-full inline-flex justify-center whitespace-nowrap rounded-lg bg-indigo-500 px-3.5 py-2.5 text-sm font-medium text-white shadow-sm shadow-indigo-950/10 hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150" href="#0">
                    Purchase Plan
                </a>
            </div>
            <div class="text-slate-900 dark:text-slate-200 font-medium mb-3">Includes:</div>
            <ul class="text-slate-600 dark:text-slate-400 text-sm space-y-3 grow">
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Unlimited placeholder texts</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Consectetur adipiscing elit</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Excepteur sint occaecat cupidatat</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Officia deserunt mollit anim</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Predefined chunks as necessary</span>
                </li>
                <li class="flex items-center">
                    <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                    </svg>
                    <span>Free from repetition</span>
                </li>
            </ul>
        </div>
    </div>
</div>
```

The component is now complete and fully functional. However, we can further refine the code.

A significant portion of it is identical for all three pricing tabs, so we can create a **reusable pricing tab component**. By passing only the properties that distinguish the individual tabs to this component, we can keep our code [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself), improve cleanliness, and enhance maintainability.

Creating a reusable pricing tab component
-----------------------------------------

To create the reusable pricing tab component, we need to create a new file in the components folder named `PricingTab.vue`. This approach differs from the Next.js project, as Vue’s template syntax requires a separate file for this component.

The properties that we’ll pass to the component are as follows:

*   `yearly`: a boolean indicating whether the pricing table is for annual or monthly pricing
*   `popular`: an optional prop indicating if the pricing tab should be highlighted as the most popular
*   `planName`: the name of the plan
*   `price`: an object containing the monthly and annual prices
*   `planDescription`: the description of the plan
*   `features`: an array of strings containing the plan’s features

Since **TypeScript** is being used, we’ll also define an interface for the props. Here’s the complete code for the `PricingTab.vue` component:

```ts
<script setup lang="ts">
interface Props {
    yearly: boolean
    popular?: boolean
    planName: string
    price: {
        monthly: number
        yearly: number
    }
    planDescription: string
    features: string[]
}
const props = defineProps<Props>()
</script>
<template>
    <div class="h-full" :class="{ 'dark': props.popular }">
        <div class="relative flex flex-col h-full p-6 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-900 shadow shadow-slate-950/5">
            <div v-if="props.popular" class="absolute top-0 right-0 mr-6 -mt-4">
                <div class="inline-flex items-center text-xs font-semibold py-1.5 px-3 bg-emerald-500 text-white rounded-full shadow-sm shadow-slate-950/5">Most Popular</div>
            </div>
            <div class="mb-5">
                <div class="text-slate-900 dark:text-slate-200 font-semibold mb-1">{{ props.planName }}</div>
                <div class="inline-flex items-baseline mb-2">
                    <span class="text-slate-900 dark:text-slate-200 font-bold text-3xl">$</span>
                    <span class="text-slate-900 dark:text-slate-200 font-bold text-4xl" v-text="yearly ? props.price.yearly : props.price.monthly"></span>
                    <span class="text-slate-500 font-medium">/mo</span>
                </div>
                <div class="text-sm text-slate-500 mb-5">{{ props.planDescription }}</div>
                <a class="w-full inline-flex justify-center whitespace-nowrap rounded-lg bg-indigo-500 px-3.5 py-2.5 text-sm font-medium text-white shadow-sm shadow-indigo-950/10 hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150" href="#0">
                    Purchase Plan
                </a>
            </div>
            <div class="text-slate-900 dark:text-slate-200 font-medium mb-3">Includes:</div>
            <ul class="text-slate-600 dark:text-slate-400 text-sm space-y-3 grow">
                <template v-for="feature in props.features">
                    <li class="flex items-center">
                        <svg class="w-3 h-3 fill-emerald-500 mr-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
                            <path d="M10.28 2.28L3.989 8.575 1.695 6.28A1 1 0 00.28 7.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 2.28z" />
                        </svg>
                        <span>{{ feature }}</span>
                    </li>
                </template>
            </ul>
        </div>
    </div>
</template>
```

Note that the features list is an array, so we can use a `<template>` tag to iterate over it using the `v-for` directive, and render each individual feature.

Now that we have the component, we can import it into the `PricingTable.vue` file and use it instead of the code that defined the individual tabs. Here’s an example:

```html
<PricingTab
    :yearly="isAnnual"
    planName="Essential"
    :price="{ yearly: 29, monthly: 35 }"
    planDescription="There are many variations available, but the majority have suffered."
    :features="[
        'Unlimited placeholder texts',
        'Consectetur adipiscing elit',
        'Excepteur sint occaecat cupidatat',
        'Officia deserunt mollit anim',
    ]" />
```

Conclusions
-----------

The `PricingTable.vue` and `PricingTab.vue` components are both ready, and `PricingTab` can be imported and used within `PricingTable`.

Here is the complete code for `PricingTable.vue`:

```ts
<script setup lang="ts">
import { ref } from 'vue'
import PricingTab from './PricingTab.vue';
const isAnnual = ref<boolean>(true)
</script>
<template>
    <div>
        <!-- Pricing toggle -->
        <div class="flex justify-center max-w-[14rem] m-auto mb-8 lg:mb-16">
            <div class="relative flex w-full p-1 bg-white dark:bg-slate-900 rounded-full">
                <span class="absolute inset-0 m-1 pointer-events-none" aria-hidden="true">
                    <span class="absolute inset-0 w-1/2 bg-indigo-500 rounded-full shadow-sm shadow-indigo-950/10 transform transition-transform duration-150 ease-in-out" :class="isAnnual ? 'translate-x-0' : 'translate-x-full'"></span>
                </span>
                                <button
                    class="relative flex-1 text-sm font-medium h-8 rounded-full focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150 ease-in-out"
                    :class="isAnnual ? 'text-white' : 'text-slate-500 dark:text-slate-400'"
                    @click="isAnnual = true"
                >Yearly <span
                    :class="isAnnual ? 'text-indigo-200' : 'text-slate-400 dark:text-slate-500'">-20%</span></button>
                <button
                    class="relative flex-1 text-sm font-medium h-8 rounded-full focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150 ease-in-out"
                    :class="isAnnual ? 'text-slate-500 dark:text-slate-400' : 'text-white'"
                    @click="isAnnual = false"
                >Monthly</button>
            </div>
        </div>
        <div class="max-w-sm mx-auto grid gap-6 lg:grid-cols-3 items-start lg:max-w-none">
            <!-- Pricing tab 1 -->
            <PricingTab
                :yearly="isAnnual"
                planName="Essential"
                :price="{ yearly: 29, monthly: 35 }"
                planDescription="There are many variations available, but the majority have suffered."
                :features="[
                    'Unlimited placeholder texts',
                    'Consectetur adipiscing elit',
                    'Excepteur sint occaecat cupidatat',
                    'Officia deserunt mollit anim',
                ]" />
            <!-- Pricing tab 2 -->
            <PricingTab
                :yearly="isAnnual"
                :popular="true"
                planName="Perform"
                :price="{ yearly: 49, monthly: 55 }"
                planDescription="There are many variations available, but the majority have suffered."
                :features="[
                    'Unlimited placeholder texts',
                    'Consectetur adipiscing elit',
                    'Excepteur sint occaecat cupidatat',
                    'Officia deserunt mollit anim',
                    'Predefined chunks as necessary',
                ]" />
            <!-- Pricing tab 3 -->
            <PricingTab
                :yearly="isAnnual"
                planName="Enterprise"
                :price="{ yearly: 79, monthly: 85 }"
                planDescription="There are many variations available, but the majority have suffered."
                :features="[
                    'Unlimited placeholder texts',
                    'Consectetur adipiscing elit',
                    'Excepteur sint occaecat cupidatat',
                    'Officia deserunt mollit anim',
                    'Predefined chunks as necessary',
                    'Free from repetition',
                ]" />
        </div>
    </div>
</template>
```

It looks great, isn’t it 🙂 If you enjoyed this tutorial, don’t forget to check out the other versions of this component in Alpine.js and Next.js.

If you’re looking for a pre-coded version of this component, we recommend having a look at our [Tailwind CSS templates](https://cruip.com/). Specifically, at this [dark Next.js landing page template](https://cruip.com/stellar/) or if you’re seeking something more minimalist, this [simple website template](https://cruip.com/simple/).