Crafting Clean Code with Tailwind CSS and Vue Composition API
Tags: tailwindcss, vue, cleancode, webdev
Published: 2024-02-05 21:18:12


In the ever-evolving landscape of web development, writing clean and maintainable code is paramount. This post will guide you through a method that combines the power of Tailwind CSS with Vue's Composition API and the Single File Component (SFC) syntax. This approach not only enhances code readability but also simplifies the process of styling Vue components.


## Project setup

Before diving into the coding process, make sure to set up your project with Tailwind CSS and Vue. We can use `npm` or `yarn` to install the necessary packages, follow these steps to set your project:

[Tailwind setup with Vue using Vite](https://tailwindcss.com/docs/guides/vite#vue)

## Leveraging Composition API and SFC Syntax

To take advantage of Vue's Composition API and SFC syntax, create a component using the `<script setup>` syntax. This modern approach simplifies the component structure and promotes cleaner code organization.

```vue
<!-- src/components/TailwindClasses.vue -->
<template>
  <div class="bg-blue-500 text-white text-xl font-bold flex items-center justify-center px-4 py-8">
    {{ message }}
  </div>
</template>

<script setup>
const message = ref('Tailwind is messy')
</script>
```

Although, the div  has multiple Tailwind CSS classes directly applied, which can become messy and hard to manage as the component grows. Consider using a more modular approach with utility classes stored in variables.

## Applying Tailwind Classes Dynamically

Lets suppose we have to set a different `color` by props. It will even more messy trying to handle variants on template. 

But we can dynamically apply Tailwind CSS classes to our component by utilizing the array variables within the template. This approach enhances maintainability and allows for easy modifications.

```vue
<!-- src/components/MyComponent.vue -->
<template>
  <div :class="computedClasses">
    {{ message }}
  </div>
</template>

<script setup>
// SECTION -  Setup and component logic
import { ref, computed, defineProps } from "vue";

const message = ref("Tailwind can be clean!");

/***
 * (optional) We will use props to pass in the color now,
 * actually this can use any other data/method to set
 * variant classes
 */
const props = defineProps({
  color: {
    type: String,
    default: "blue",
  },
});

// SECTION -  Style classes

// Combine classes using computed
const computedClasses = computed(() => {
  // Defining colors we want
  const colors = {
    blue: ["bg-blue-500", "text-white"],
    red: ["bg-red-500", "text-white"],
    yellow: ["bg-yellow-500", "text-black"],
  };

  return [
    colors[props.color],
    "text-xl font-bold",
    "px-4 py-8",
    "flex items-center justify-center",
  ];
});
</script>

```

Separating component logic and style classes into distinct sections with comments makes it easier for developers to understand and maintain the code, promoting a clean and modular structure.

>The use of clear annotations, such as "SECTION" is a great practice to enhance code organization and readability. 
> You can use a extension like [Comment Anchors](https://marketplace.visualstudio.com/items?itemName=ExodiusStudios.comment-anchors) or [Better Comments](https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments) to highlight sections on VSCode.


## Conclusion

By combining Tailwind CSS, Vue's Composition API, and the SFC syntax, you can write clean, modular, and maintainable code. Integrating Tailwind CSS with Vue using the Composition API and SFC syntax is a powerful combination. It provides a clear and concise way to handle styles while maintaining a high level of flexibility and readability. This approach aligns well with the principles of clean code, making it a recommended choice for Vue developers.

You can check this example on [CodeSandbox](https://codesandbox.io/p/devbox/vue-tailwind-whvdv2?file=%2Fsrc%2Fcomponents%2FTailwindClasses.vue%3A32%2C24&layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522cls9cwpn800073b6tfier1cjl%2522%252C%2522sizes%2522%253A%255B70%252C30%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522cls9cwpn700023b6tgna1tbjr%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522cls9cwpn700043b6t5z17idp9%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522cls9cwpn700063b6tuhhw060u%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B50%252C50%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522cls9cwpn700023b6tgna1tbjr%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cls9cwpn700013b6tc12852wq%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252FREADME.md%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522id%2522%253A%2522cls9da0pp00023b6tmmln5209%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522initialSelections%2522%253A%255B%257B%2522startLineNumber%2522%253A2%252C%2522startColumn%2522%253A22%252C%2522endLineNumber%2522%253A2%252C%2522endColumn%2522%253A22%257D%255D%252C%2522filepath%2522%253A%2522%252Fsrc%252Fmain.ts%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522id%2522%253A%2522cls9dciv500023b6tjftqtv6t%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522initialSelections%2522%253A%255B%257B%2522startLineNumber%2522%253A32%252C%2522startColumn%2522%253A24%252C%2522endLineNumber%2522%253A32%252C%2522endColumn%2522%253A24%257D%255D%252C%2522filepath%2522%253A%2522%252Fsrc%252Fcomponents%252FTailwindClasses.vue%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%255D%252C%2522id%2522%253A%2522cls9cwpn700023b6tgna1tbjr%2522%252C%2522activeTabId%2522%253A%2522cls9dciv500023b6tjftqtv6t%2522%257D%252C%2522cls9cwpn700063b6tuhhw060u%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cls9cwpn700053b6tx6hkwb5h%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_PORT%2522%252C%2522taskId%2522%253A%2522pnpm%2520run%2520dev%2522%252C%2522port%2522%253A5173%252C%2522path%2522%253A%2522%252F%2522%257D%255D%252C%2522id%2522%253A%2522cls9cwpn700063b6tuhhw060u%2522%252C%2522activeTabId%2522%253A%2522cls9cwpn700053b6tx6hkwb5h%2522%257D%252C%2522cls9cwpn700043b6t5z17idp9%2522%253A%257B%2522id%2522%253A%2522cls9cwpn700043b6t5z17idp9%2522%252C%2522activeTabId%2522%253A%2522cls9cwpn700033b6trtkd3zhz%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cls9cwpn700033b6trtkd3zhz%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522pnpm%2520run%2520dev%2522%257D%252C%257B%2522id%2522%253A%2522cls9d4x1g00qw3b6tx6lobzxn%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TERMINAL%2522%252C%2522shellId%2522%253A%2522cls9d4xbx002pdxhuc5mz2esx%2522%257D%255D%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D)