DEV Community

Cover image for Setting up Tailwind with Vue.js
James Perkins for Sanity.io

Posted on • Originally published at sanity.io

Setting up Tailwind with Vue.js

Summary

In this guide, you will add Tailwind to a Vue application to improve the layout for our blog pages. You will go through dependencies, configuration, and component creation. To follow this tutorial you will need a basic understanding of Vue.

Why use TailwindCSS?

In 2019‚ and even more so in 2020, Tailwind CSS exploded in popularity when developers saw the potential in a design system that was more like an API. Unlike Bootstrap, which provides pre-built components, Tailwind CSS provides utility classes to build your own components that are completely responsive and let developers create exactly what they need. Tailwind and Vue integrate together with just a few packages, making it a go-to option for any developer.

What are utility classes?

Utility classes are single purpose classes which are self descriptive in nature, unlike traditional CSS classes. For example:

.flex: {
    display: flex
}
Enter fullscreen mode Exit fullscreen mode

This example describes that you are adding flex, and only adds display flex to an element. This makes it reusable anywhere in the code and know exactly what it does.

Finished project link

To follow along exactly with the blog post you can find the repository on my GitHub by clicking this

link.

Setup

Add the dependencies

To begin with, you need to install the dependencies for Tailwind. Inside your terminal or command prompt, from the root of your project, type the following:

npm install tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
Enter fullscreen mode Exit fullscreen mode

First, you are probably wondering why you are versioning, well there is (as of April 2021) a compatibility issue between tailwindcss and postcss which you can read about here. So what do these dependencies do? Let's break them down:

tailwindcss : Contains all the Tailwind code that you can use in our Vue application.

postcss : PostCSS is used to transform CSS using a variety of JS plugins, it's a one stop-shop for being able to lint your CSS, support variables and mixins, transpile future CSS syntax, inline images, and more.

autoprefixer : AutoPrefixer is a plugin for postCSS that parses all of your CSS and adds the vendor prefixes to your CSS rules. This means you don't have to worry whether you are using Microsoft Edge , Firefox, or Chrome!

Creating Tailwind configuration files

Now you have all of the dependencies installed you need to create two configuration files that will handle both what happens with PostCSS and any configuration you want to make with Tailwind such as themes, dark mode etc. At the root of your project create a tailwind.config.js and postcss.config.js and let's take a deep dive into what you have, first let's open the newly created tailwind.config.js file and create our default configuration:

// ./tailwind.config.js
module.exports = {
  purge: [],
  darkMode: false,
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}
Enter fullscreen mode Exit fullscreen mode

Tailwind is built on the premise of building customized user interfaces from the ground up, in this file you can create a whole theme, use plugins, set default colors and so much more. In this tutorial, you will be using the purge option only. The purge option allows you to put in all of your files that contain CSS and as part of the build step will removed any used styles. You can learn more about configuring Tailwind in the configuration documentation. Let's move on to the postcss.config.js file and enter the following :

// ./postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}
Enter fullscreen mode Exit fullscreen mode

As mentioned earlier, you use PostCSS to transform CSS using a variety of JS plugins, in this case when our code is built PostCSS will handle both Tailwind CSS and any prefixes for browsers without any intervention from us!

Configure Tailwind to remove any unused styles in production

The final piece of our configuration is to have Tailwind purge any unused styles when in production to keep our bundle size as small as possible, to do this open up your tailwind.config.js file and type the following:

// ./tailwind.config.js
module.exports = {
   purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
    darkMode: false,
    theme: {
      extend: {},
    },
    variants: {
      extend: {},
    },
    plugins: [],
  }
Enter fullscreen mode Exit fullscreen mode

What you are doing here is telling Tailwind where the paths to all our components and pages so Tailwind can remove all unused styles using a technique called tree-shaking, which deletes all the unused styles.

Adding Tailwind to our CSS

Now that you have installed and set up everything required to start using Tailwind you need to create a way to use it globally. So let's create an index.css in your src folder to hold our Tailwind styles:

<!-- ./src/main.css -->
@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

This might seem like you are going to have every single Tailwind style in our application but at build time Tailwind is smart enough to styles generated based upon your configuration.

Import your CSS file into main.js

The final setup piece is to import the CSS file into our main.js which is located at ./src/main.js and once imported your file should look like:

// ./src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

createApp(App).mount('#app')
Enter fullscreen mode Exit fullscreen mode

Tailwind is now set up and referenced, so you are ready to create a great looking blog page.

If you are following along here is the commit

Creating Components

For this tutorial, you are going to create a navigation bar and a blog page which will be our home page, the blog posts won't actually exist but it will give you a great starting point for your own blog post.

Creating a Navigation Bar.

Let's begin by creating a responsive navigation header that you can use on each page. Let's have create some starter code that doesn't have any Tailwind attached, create a new component called NavBar:

<!--./src/components/NavBar.vue -->
<template>
  <header>
    <div>
      <nav>
        <ul>
          <li><router-link to="/">Home</router-link></li>
          <li><router-link to="/">Blog</router-link></li>
          <li><router-link to="about">About</router-link></li>
        </ul>
      </nav>
    </div>
  </header>
</template>

<script>
export default {
  name: "NavBar",
};
</script>
Enter fullscreen mode Exit fullscreen mode

Now you have this new NavBar component let's use it in our application by adding it to our App.vue file. Inside the App.vue file remove the original div above the router-view below is the before and after:

<!-- ./src/App.vue -->

<!--before -->
<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>

<!-- after --> 
<template>
  <div id="app">
    <router-view />
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

You are now ready to place use our own NavBar component. To import a Component you need to add a script tag under our template and then register the template copy the code below :

// ./src/App.vue
...
<script type="text/javascript">
import NavBar from "@/components/NavBar.vue";
export default {
  components: {
    NavBar, // register component
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Then you can add this into our template, so before the router-view add in the NavBar so your finished code should look like the following:

// ./src/App.vue
<template>
  <div id="app">
    <NavBar />
    <router-view />
  </div>
</template>

<script type="text/javascript">
import NavBar from "@/components/NavBar.vue";
export default {
  components: {
    NavBar, // register component
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Now launch your code and navigate to localhost:8080 and you should see the following:

NavBar

It doesn't look great but the links to Home and About work as expected so let's add some Tailwind to this to make a navigation bar with responsiveness!

Adding Tailwind to our component

Open up your NavBar component and let's start adding some classes from Tailwind, first you want to add a border to the bottom to add some division, and then you will add some flex to our surrounding div:

// ./src/components/NavBar.vue
<template>
  <header class="border-b">
    <div
      class="container flex flex-col flex-wrap p-5  md:items-center md:flex-row"
    >
....
Enter fullscreen mode Exit fullscreen mode

So let's explain what is happening here,

  • Under the header, you are adding a class="border-b" which is a border on the bottom.
  • Then in the div you are adding the container class.
  • Adding flex and any screen, bigger than medium size will be flex-col with flex wrap.
  • Adding p-5 which adds padding: 1.25rem

The part md:items-center md:flex-row means when the size reduces to a breakpoint for medium screens, it will center the items and make them into a row. You can read about responsive designs and conditional breakpoints in the link below:

https://tailwindcss.com/docs/responsive-design

Next, you can add flex to our <ul> and some margin and font styles to our links to make sure you have a functional and responsive Navbar.

// ./src/components/NavBar.vue
<ul class="flex flex-wrap items-center justify-center text-base ">
          <li class="mr-5 text-sm font-semibold hover:text-gray-800">
            <router-link to="/">Home</router-link>
          </li>
Enter fullscreen mode Exit fullscreen mode

So this code, allows the items to wrap as needed, and on the individual items you are adding a margin on the right, making the font bold and changing the state when you hover over them, at this point your code should look like the following:

// ./src/components/NavBar.vue
<template>
  <header class="border-b">
    <div
      class="container flex flex-col flex-wrap p-5  md:items-center md:flex-row "
    >
      <nav>
        <ul class="flex flex-wrap items-center justify-center text-base ">
          <li class="mr-5 text-sm font-semibold hover:text-gray-800">
            <router-link to="/">Home</router-link>
          </li>
          <li class="mr-5 text-sm font-semibold hover:text-gray-800">
            <router-link to="/">Blog</router-link>
          </li>
          <li class="mr-5 text-sm font-semibold hover:text-gray-800">
            <router-link to="about">About</router-link>
          </li>
        </ul>
      </nav>
    </div>
  </header>
</template>

<script>
export default {
  name: "NavBar",
};
</script>
Enter fullscreen mode Exit fullscreen mode

If you are following along here is the commit.

You should now have a navbar that looks like this on your desktop:

Desktop Version of Nav

Then when you shrink the screen size down and hit the media point you should now have a navbar that looks like:

Smaller Version of Nav

Looking pretty good! A nice NavBar component to help our users navigate our website.

Creating the blog post page

As I explained previously, you aren't creating any blog posts but you can create a "fake" Blog page that will show the user a cover image, title, and excerpt. To make this easier below is the code will be using but first let's create a new component called BlogPage which will handle that in our components directory.

<!-- ./src/components/BlogPage.vue -->
<template>
  <div id="blog-home">
    <h1>Thoughts and Posts</h1>
    <div>
      <article>
        <figure>
          <img src="http://via.placeholder.com/250x250" alt="" />
        </figure>
        <h2>A blog post about my dog</h2>
        <p>Super awesome doggo, with a great smile.</p>
      </article>
    </div>
    <div>
      <article class="media">
        <figure>
          <img src="http://via.placeholder.com/250x250" alt="" />
        </figure>
        <h2>Post about some code we wrote</h2>
        <p>Sometimes coding is very hard, and othertimes it's very easy</p>
      </article>
    </div>
    <div>
      <article class="media">
        <figure>
          <img src="http://via.placeholder.com/250x250" alt="" />
        </figure>
        <h2>Titles are hard to think of.</h2>
        <p>Just like naming variables, naming title are hard.</p>
      </article>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

This has three basic blog posts with a title, image holder and a short excerpt, you now have a component but you need to create a route and view so our users can navigate. Create a new file inside your views folder called Blog.vue, this will handle what is displayed to the user when they navigate to the route.

The view template will be simple because you created a component and don't have to worry about creating any more HTML so below is the template part

<!-- ./src/views/Blog.vue-->
<template>
  <div class="blog">
    <BlogPage />
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Similar to App.vue you are registering the component to use it. Now you need to add a route to our routes file, which is located under router named index.js . To add the router you can copy the about route.

Now you need to import the BlogPage component and reference it so you can actually use it in our view.

<!-- ./src/views/Blog.vue-->
...
<script>
import BlogPage from "@/components/BlogPage.vue";

export default {
  name: "Blog",
  components: {
    BlogPage,
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode
<!-- ./src/router/index.js -->
{
    path: "/blog",
    name: "Blog",
    component: () => import(/* webpackChunkName: "blog" */ "../views/Blog.vue"),
  },
Enter fullscreen mode Exit fullscreen mode

If you are following along you can check out this commit.

Go ahead and launch your application and navigate to http://locahost:8080/blog and you should now see the following:

Post before Responsive

This is not pretty or responsive, so let's start adding our Tailwind to make a blog post page look great!

Adding Tailwind to the blog post.

So you are going to create a grid style blog page where each post takes up 1/3 of the width of the screen. To begin with let's center our Heading and increase the size and add a margin to the top:

<!--./src/components/BlogPage.vue-->
<template>
  <div id="blog-home">
    <h1 class="text-center text-xl mt-4">Thoughts and Posts</h1>
...

Enter fullscreen mode Exit fullscreen mode

This will give our heading a nice centered look and increase the size.

Heading

Now you can create a container using the next main and add some padding around the top and sides.

<!--./src/components/BlogPage.vue-->
<template>
  <div id="blog">
    <h1 class="text-center text-xl mt-4">Thoughts and Posts</h1>
    <main class="container px-8 pt-24 mx-auto lg:px-4">
.....
Enter fullscreen mode Exit fullscreen mode

This gives us the container class padding on the sides and top, margin on the x-axis to auto and on large screens, you will also at padding on the x-axis.

You now need to add an additional div surrounding our article, which will handle flex for us. So create a new div and add the classes flex flex-wrap.

<!--./src/components/BlogPage.vue-->
<template>
  <div id="blog">
    <h1 class="text-center text-xl mt-4">Thoughts and Posts</h1>
    <main class="container px-8 pt-24 mx-auto lg:px-4">
      <div class="flex flex-wrap">
....
Enter fullscreen mode Exit fullscreen mode

Now you have our container, and flex div you can start working on our articles, now in the real world, you would only need to create a single one and loop over all of your articles. So I will show you how to create a single version of it, and then I will show you a complete version.

On our article tag let's add the classes for padding, and width for both large screens and the default sizing.

<!--./src/components/BlogPage.vue-->
<article class="px-8 py-6 lg:w-1/3 md:w-full">
Enter fullscreen mode Exit fullscreen mode

This will add padding on the sides and top and bottom, on a large screen it will take 1/3 of the width and on smaller than large screens it will create a full width, this will allow us to create a column effect when the screen is smaller.

You can now add padding to the div that contains all of our blog post information:

<!--./src/components/BlogPage.vue-->
<article class="px-8 py-6 lg:w-1/3 md:w-full">
          <div class="p-6 rounded-md">
...
Enter fullscreen mode Exit fullscreen mode

Now for our cover image you are going to make the image rounded also and specific height so no matter what size image it will always look great!

<article class="px-8 py-6 lg:w-1/3 md:w-full">
          <div class="p-6 rounded-md">
            <img
              class="object-cover object-center w-full h-40 mb-6 rounded"
              src="http://via.placeholder.com/250x250"
              alt="content"
            />
Enter fullscreen mode Exit fullscreen mode

So you are creating a full width and height of h-40 (10rem) you are also using object-cover which will make the image cover the container. Finally you are rounding the corners, your images should now look like:

Rounded Corner Images

For our blog title will will make the the text have a margin on the bottom and the text larger so it pops for our users:


<article class="px-8 py-6 lg:w-1/3 md:w-full">
          <div class="p-6 rounded-md">
            <img
              class="object-cover object-center w-full h-40 mb-6 rounded"
              src="http://via.placeholder.com/250x250"
              alt="content"
            />
            <h2 class="mb-3 text-lg lg:text-2xl">
              A blog post about my dog
            </h2>
Enter fullscreen mode Exit fullscreen mode

As you can see for any screen it should have large text, and only on large screen it should be 2xl, now you just need to take care of our excerpt and link to read more.

To handle our paragraph that is an excerpt you are going to use leading-relaxed which actually means you are going to a 1.25 rem to our line height and you will add a margin at the bottom also.

<!--./src/components/BlogPage.vue-->
<article class="px-8 py-6 lg:w-1/3 md:w-full">
          <div class="p-6 rounded-md">
            <img
              class="object-cover object-center w-full h-40 mb-6 rounded"
              src="http://via.placeholder.com/250x250"
              alt="content"
            />
            <h2 class="mb-3 text-lg lg:text-2xl">
              A blog post about my dog
            </h2>
            <p class="mb-4 leading-relaxed">
              Super awesome doggo, with a great smile.
            </p>
Enter fullscreen mode Exit fullscreen mode

So the final piece to the puzzle is the link to "read more" , you are going to add some hover color change and then add some margin on the bottom, you are also going to make it inline-flex and center the items.

<article class="px-8 py-6 lg:w-1/3 md:w-full">
          <div class="p-6 rounded-md">
            <img
              class="object-cover object-center w-full h-40 mb-6 rounded"
              src="http://via.placeholder.com/250x250"
              alt="content"
            />
            <h2 class="mb-3 text-lg lg:text-2xl">
              A blog post about my dog
            </h2>
            <p class="mb-4 leading-relaxed">
              Super awesome doggo, with a great smile.
            </p>
            <a
              href="#"
              class="inline-flex items-center md:mb-2 lg:mb-0 hover:text-blue-400 "
            >
              Read More
            </a>
          </div>
        </article>
Enter fullscreen mode Exit fullscreen mode

You now have a complete article , so you can find the final commit here.

If you launch your application you should now see the following:

Desktop Complete

and on mobile you will see this:

Mobile Complete

Conclusion

You have now come to the end of our tutorial and created a blog page example using Tailwind, if you have been coding along while reading you would have learnt the following:

  1. Learnt how to install TailwindCSS and PostCSS and setup our Vue application to use them.
  2. Create a responsive Navigation Bar that allows your end users to navigate regardless of the size of their screen.
  3. Create a fully responsive Blog page with styled images, headings and excerpts and the option to read more.

Now you have a good base layer, you can start adding your own post to your Vue application and explore using the ability to create your own themes.

Top comments (0)