March 31, 2021

Setting up Tailwind with Vue.js

By James Perkins

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
}

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 knows 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

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: [],
}

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 remove 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: {},
  },
}

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: [],
  }

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;

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')

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>

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>

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>

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>

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

Image of NavBar on Desktop

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

Mobile version of our Navbar

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 we'll 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>

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>

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>

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.

// ./src/router/index.js

{
  path: "/blog",
  name: "Blog",
  component: () => import(/* webpackChunkName: "blog" */ "../views/Blog.vue"),
},

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:

Our blog post before Tailwind

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

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

Image of our new 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">
.....

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">
....

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">

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">
...

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"
    />

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

As you can see for any screen it should have large text, and only on a 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>

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>

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:

Finished blog on desktop

and on mobile you will see this:

Finished blog on mobile

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 learned the following:

  1. Learned 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.

Sanity.io: Content Is Data

Sanity.io is a platform to build websites and applications. It comes with great APIs that let you treat content like data. Give your team exactly what they need to edit and publish their content with the customizable Sanity Studio. Get real-time collaboration out of the box. Sanity.io comes with a hosted datastore for JSON documents, query languages like GROQ and GraphQL, CDNs, on-demand asset transformations, presentation agnostic rich text, plugins, and much more.

Don't compromise on developer experience. Join thousands of developers and trusted companies and power your content with Sanity.io. Free to get started, pay-as-you-go on all plans.