How to create a responsive navigation menu in Tailwind CSS?

Sunday, February 7, 2021 - Reading time: 17 minutes.

In this tutorial we’re creating a fully responsive navigation menu with Tailwind CSS. I will teach you how you can leverage Tailwinds utility classes to create a horizontal menu layout that transforms into a vertical hamburger style menu on smaller screen sizes.

ow to create a responsive navigation menu in Tailwind CSS?

We add Alpine.js into the mix for creating the toggle functionality for our hamburger menu, so that the menu can actually be opened and closed. For those who never heard about Alpine.js, don’t worry, it is a small JavaScript library and you could argue that it is a modern version of jQuery.

Why don’t we start with laying out the HTML structure for the responsive navigation menu.

Building the HTML structure for our menu

When building a responsive navigation menu you have basically two options. Create two separate HTML structures, one for your desktop view and another one for your mobile view. Another option is to create a single HTML structure that can be used for both desktop and mobile.

In this tutorial we go with the second option, combining the HTML structure for our desktop and mobile menu. I believe that it’s cleaner to have only one navigation bar component in your document. The downside of this approach is that it’s harder to build, in my opinion a small sacrifice to make.

Feel free to code along or use our responsive code preview to see the progress that we make along to our responsive navigation.

Basic HTML Structure for responsive navigation


Adding Tailwind CSS classes for our horizontal menu

If you code along, make sure that you first have properly added Tailwind CSS to your website. If you don’t know how to add Tailwind CSS check out the official documentation for the recommended installation method suited for your situation.

Styling the basic navbar layout

We are building a classic layout where our logo is on the left and our navigation is on the right. We are using Tailwinds flexbox utility classes for this. We are telling the header element to use flexbox and we want it to behave like a flex-row. Then we tell it to push the items inside to their opposite sides with justify-between and we add a minimum spacing between the children with space-x-4.

We set the background for our header to white with bg-white and set a padding on all sides with py-6 (short for top and bottom) and px-6 (short for left and right).

Finally we make sure that our logo is the right height (h-8,) and that the descriptive text is only accessible for screen readers. The sr-only utility class makes any element only accessible for screen readers and than hides it from the screen.

Basic navbar styling


Now that we built our basic layout, let’s start with making the navigation links a little more pleasing to the eyes. First we apply various flex-box utility classes to our nav element and render all content with a semibold font-weight (font-semibold).

Next we specify the text colors for the navigation links. Active links are marked with a text-indigo-600 class and inactive links are marked with the text-gray-600 class. When hovering over the navigation links we show an underline (hover:underline). That is all you need to build a simple horizontal navigation.

Horizontal menu finished


If you don’t care about your mobile visitors, then you can now stop following along. At this point you created a perfectly fine navigation menu bar in Tailwind CSS. But chances are that you are reading this article because you are looking for a way to create a responsive navigation menubar.

Make the navigation responsive with breakpoints

Okay let’s now figure out how we can transform our horizontally menu to a vertical hamburger menu on smaller screens. For this we want to display a hamburger menu icon next to our logo, so that users are able to click the hamburger menu button to toggle the navigation dropdown.

Start with creating a rough draft

It’s always a good practice when you are building something complex to tackle the problem one step at the time. We start to modify our header element first. Items inside a flexbox container have the default behavior to resize their widths to fit the parent element. Most of the time this is fine, but in our situation we aren’t looking for this behavior.

We first want to display our navigation dropdown below our logo. To force this we have to tweak a few things. First We add a flex-wrap class to our header, this forces elements to wrap (basically move it a row below) when they don’t fit anymore inside the parent element. We also make sure that we only center (md:items-center) and space (md:space-x-4) elements on larger screens. Lastly we add a relative class, so that we can later better position elements based on the header position and dimension.

Vertical menu rough draft


Next we add a background (bg-gray-100) to our dropdown and make sure that the background is not visible on larger screens (md:bg-transparent). We set different padding for smaller screens (p-6) and reset it for larger screens (md:p-0).

We also want our navigation links to sit below of each other. Therefore we change the flexbox direction to column layout (flex-col) on smaller screens. Don’t forget to restore it back to a flex-row on larger screens. Lastly we want our dropdown to take full width (w-full) and set the width automatically on larger screens (w-auto).

Introducing the hamburger and making the menu pretty

The rough draft is in place, time to make the menu dropdown pretty. Before we start with that’, let’s make sure that we add the hamburger menu button. We want the button to be an inline-block element and we don’t want it visible on larger screens (md:hidden).

We set the height (h-8) and width (w-8) and give the button a bg-gray-200 and text-gray-600 classes. Finally we add a little padding (p-1) to the button. For the icon we are using the hamburger icon from the heroicons library.

Introducing the Hamburger


Now the difficult part, making the dropdown prettier. We have to change a lot of classes and the total amount of classes can be a little overwhelming. Don’t get intimidated by it.

First we want our dropdown to be positioned absolute, so that it sits on top of the rest of the content. On larger screens we want it to behave like a normal html element and reset it’s behavior with md:relative. With the classes top-16 and left-0 we determine the position of the dropdown and with md:top-0 we reset the position back to normal on larger screens.

We swap out our gray background for a white background (bg-white), add shadow to the dropdown (shadow-md) and remove it on larger screens (md:shadow-none). Finally we round the borders (rounded-lg) of the dropdown menu and reset it on larger screens (md:rounded-none).

Notice that I also added extra dummy content to the page. This way we can better demonstrate the working of the dropdown menu.

Creating the toggle functionality with Alpine.js

Our mobile menu now looks pretty but it’s always expanded at the moment. That is not what we are looking for. Let’s build the ability for the user to toggle the menu open and closed. This is the part where we let Alpine.js shine. Add the following code to the head of your page. Make sure that you installed it correctly, checkout the documentation if you need help.

<script src="" defer></script>

We need a way to keep track if our dropdown menu is open or closed. We can do that in Alpine.js with the x-data property. We are using this property as a mini local database to store the dropdown menu state. We assign a mobileMenuOpen key in our little database and set it by default to false. Meaning that the menu dropdown is closed by default.

Adding Alpine.js into the mix


Now we need a way to change the stored value to true. We only want to show the dropdown menu when the user clicks the hamburger button. Alpine.js gives us a way to listen to a click event with the special @click property. This way we can say when a user clicks the button toggle the MobileMenuOpen value to it’s opposite. We do this as follow:

@click="mobileMenuOpen = !mobileMenuOpen"

Next we need a way to dynamically change the classes used on the nav element. We want to add the hidden class when the mobileMenuOpen value is false and add the flex class when the mobileMenuOpen value is set to true. We can easily do this with the following code:

:class="{ 'flex' : mobileMenuOpen , 'hidden' : !mobileMenuOpen}"

Lastly we want to make sure that the menu is always visible on larger screens, we do this by adding the md:flex class to the nav element. And we leverage a special Alpine.js click event listener named @click.away. This gives us an option to do something when the user clicks outside the dropdown menu. In our case we want to set the mobileMenuOpen to false to hide the dropdown menu.

@click.away="mobileMenuOpen = false"

Final Responsive Menu built with Tailwind CSS

There we have it. Our fully completed responsive navigation menu built in Tailwind CSS and Alpine.js. Feel free to use this menu on your own website. I hope that you liked this tutorial and please share it with your friends, colleagues and followers. That would mean a lot to me.

Final Tailwind CSS responsive menu


Looking for more Tailwind CSS examples and tutorials?

Here at we really like Tailwind CSS. We made a whole bunch of free Tailwind CSS components & themes and we recently started working on our first premium Tailwind CSS product. Visit our blog page for more awesome tutorials.

Maybe you’re interested in learning how to set Typographic defaults in Tailwind CSS? Or are you looking for a way to easily embed responsive YouTube videos into your website with Tailwind CSS?

If you enjoyed this article, please consider subscribing to our Tailwind CSS Newsletter. I will send you a emailwith our best articles. Tips, news, updates, snippets and all the best freebies, all yours, every month!

Written by Frank Spin @frankspin
WindyBlocks Prototyping Kit for Tailwind CSS
WindyBlocks - Prototyping kit for Tailwind CSS

150+ responsive blocks to save you massive time during development! Get early access now for just €49