If you want to build your own mega menu with numerous items, images, and lists, flexbox is the right tool for you. It will allow you to create a content-aware, multi-column, and multi-level mega menu that drops down on hover–without any JavaScript or CSS tweaks.
In this tutorial, I’ll show you how to create the following dropdown mega menu using the flexbox layout module:

This is not an easy layout, so we will create it using the following steps:
- Firstly we mark up and style the main menu bar.
- We then create a flexbox template for the mega menu with the sections you can see on the screenshot above: Holiday types, Holiday packages, Our services, Last minute offers, and Trending destinations.
- We insert the content (images,
<ul>
lists, icons) into the template. - Next, we align the items inside each section.
- We remove the template’s helper styles (background colors and borders) from the CSS.
- We add the hover rule that will display the mega menu when the user hovers over the Holidays menu.
If you want to test our flexbox mega menu before getting started, check out the following demo:
Developing a Mega Menu for WordPress?
UberMenu (WordPress Mega Menu Plugin) is one of the biggest-selling items on CodeCanyon and does absolutely all the heavy lifting for you if you’re building a mega menu for WordPress. Take a look (and then return to the tutorial, obviously!)

1. Create the Main Menu Bar
The main menu bar consists of five items: Home, Holidays, About, Blog, and Contact. The dropdown mega menu will belong to Holidays, while the other menu items will just stand on their own. Here’s the HTML that marks up the main menu bar (but not the mega menu yet):
<nav> <ul class="menu"> <li class="menu-item"><a href="#">Home</a></li> <li class="menu-item"><a href="#">Holidays</a></li> <li class="menu-item"><a href="#">About</a></li> <li class="menu-item"><a href="#">Blog</a></li> <li class="menu-item"><a href="#">Contact</a></li> </ul> </nav>
In the CSS, we’ll now define the basic styles and colors. We’ll also turn the .menu
element into a flex container so that the menu items can line up in a straight row. By adding the center
value to the align-items
property, we center each item vertically inside the flex container, too:
/* General styling */ * { box-sizing: border-box; margin: 0; padding: 0; font-family: sans-serif; } .menu li { padding: 10px; } ul { list-style-type: none; } a { color: white; text-decoration: none; } a:hover { text-decoration: underline; } img { max-width: 100%; } /* Flexbox rules */ .menu { background-color: darkslateblue; display: flex; align-items: center; text-align: center; width: 90vw; margin: 20px 5vw; height: 60px; } .menu-item { flex: 1; }
We also set the flex: 1;
rule on the menu items to make them span across the entire width of the flex container and share space among them equally. If you want to know more about how the flex
shorthand works, check out my article on flexbox sizing properties:
Below, you can see how the main menu bar looks now:

2. Create a Template for the Mega Menu
To get the layout of the mega menu right, we create a template first, then populate it with content. As our mega menu consists of multiple levels of unordered lists, it’s much easier to start by laying out the main sections. This is how our HTML changes:
<nav> <ul class="menu"> <li class="menu-item"><a href="#">Home</a></li> <li class="menu-item"><a href="#">Holidays</a> <ul class="submenu"> <li class="submenu-item"> <ul class="submenu-top"> <li class="submenu-top-item">Holiday types (links with thumbnail images)</li> <li class="submenu-top-item">Holiday packages (links with descriptions)</li> <li class="submenu-top-item">Our services (links with icons)</li> <li class="submenu-top-item">Last minute offers (links)</li> </ul><!-- End .submenu-top --> </li><!-- End .submenu-item--> <li class="submenu-item"> <ul class="submenu-bottom"> <li class="submenu-bottom-item">Trending destination 1</li> <li class="submenu-bottom-item">Trending destination 2</li> <li class="submenu-bottom-item">Trending destination 3</li> <li class="submenu-bottom-item">Trending destination 4</li> </ul> </li><!-- End .submenu-item --> </ul> </li> <li class="menu-item"><a href="#">About</a></li> <li class="menu-item"><a href="#">Blog</a></li> <li class="menu-item"><a href="#">Contact</a></li> </ul> </nav>
As you can see, .submenu
has two parts: .submenu-top
for the four list-based sections (Holiday types, Holiday packages, Our services, Last minute offers) and .submenu-bottom
for the image-based Trending destinations. They will be two separate flex containers so that we can apply different flexbox sizing and alignment rules to each.
Below, you can see the layout we want to achieve using flexbox. I’ve added colors and borders to the template so that you can see how the different flex items relate to each other (the template uses the HTML above):

The first row is the main menu; we already created and styled this in Step 1. The second row is .submenu-top
which is a flex container that lets the items take as much space as they need based on their content (content-awareness). And, the third row is .submenu-bottom
which is a flex container that divides the space equally between the items.
Beyond this, we position .submenu
relative to .menu
using the position
, top
, and left
properties. As .menu
is 60px high, we move .submenu
60px down relative to its position. The CSS below also contains the aforementioned styles belonging to .menu
and .menu-item
, however it adds position: relative;
to .menu
, too.
.menu { display: flex; align-items: center; text-align: center; width: 90vw; margin: 20px 5vw; height: 60px; position: relative; } .menu-item { flex: 1; } .submenu { width: 90vw; position: absolute; top: 60px; left: 0; text-align: left; display: flex; flex-direction: column; } .submenu-item { padding: 15px; } .submenu-top { display: flex; justify-content: space-around; } .submenu-bottom { display: flex; } .submenu-bottom-item { flex: 1; }
We display the two submenus (.submenu-top
and .submenu-bottom
) as a column-based flex layout by adding the flex-direction: column
rule to .submenu
. At the same time, both .submenu-top
and .submenu-bottom
are row-based flex layouts for their own child elements as well. We don’t add flex-direction: row;
to the code because this is the default value for flex-direction
, we just set the display
properties of .submenu-top
and .submenu-bottom
to flex
.
As we want to make .submenu-top
content-aware, we need to allow flex items to stretch as much as they need, so we don’t define the flex
property here (it will use its defaults). However, we want the items to properly line up next to each other, so we add justify-content: space-around;
to .submenu-top
. In this way, the positive space will be evenly distributed between the flex items.
The layout of .submenu-bottom
is identical to the layout of the main menu (.menu
). So, we use the flex: 1;
rule on the items to make them equally share the entire width of the flex container among each other.
In the demo below, you can see how the different sections of our mega menu line up compared to each other:
3. Insert Content into the Template
In this step, we won’t do anything but insert the final content into our mega menu template. In .submenu-top
, each flex item gets its own unordered list, while .submenu-bottom
is just one unordered list for the four trending destinations:
<nav> <ul class="menu"> <li class="menu-item"><a href="#">Home</a></li> <li class="menu-item"><a href="#">Holidays</a> <ul class="submenu"> <li class="submenu-item"> <ul class="submenu-top"> <li class="submenu-top-item thumb-list"> <a href="#" class="submenu-title">Holiday Types</a> <ul class="submenu-list"> <li> <img class="submenu-thumbnail" src="https://picsum.photos/id/1067/32/32" alt="City breaks"> <a href="#">City breaks</a> </li> <li> <img class="submenu-thumbnail" src="https://picsum.photos/id/173/32/32" alt="Adventure tours"> <a href="#">Adventure tours</a> </li> <li> <img class="submenu-thumbnail" src="https://picsum.photos/id/1038/32/32" alt="Cruises"> <a href="#">Cruises</a> </li> <li> <img class="submenu-thumbnail" src="https://picsum.photos/id/1015/32/32" alt="Beach holidays"> <a href="#">Beach holidays</a> </li> </ul> </li> <li class="submenu-top-item desc-list"> <a href="#" class="submenu-title">Holiday packages</a> <ul class="submenu-list"> <li> <a href="#">Families</a> <span class="submenu-desc">Take advantage of our special holiday packages for families.</span> </li> <li> <a href="#">Students</a> <span class="submenu-desc">Take advantage of our special holiday packages for students.</span> </li> <li> <a href="#">Couples</a> <span class="submenu-desc">Take advantage of our special holiday packages for couples.</span> </li> </ul> </li> <li class="submenu-top-item icon-list"> <a href="#" class="submenu-title">Our services</a> <ul class="submenu-list"> <li> <i class="fas fa-plane-departure"></i> <a href="#">Plane tickets</a> </li> <li> <i class="fas fa-car"></i> <a href="#">Car rental</a> </li> <li> <i class="fas fa-luggage-cart"></i> <a href="#">Luggage pickup</a> </li> <li> <i class="fas fa-phone-alt"></i> <a href="#">24/7 customer service</a> </li> <li> <i class="fas fa-dollar-sign"></i> <a href="#">30-day cancellation policy</a> </li> </ul> </li> <li class="submenu-top-item"> <a href="#" class="submenu-title">Last minute offers</a> <ul class="submenu-list"> <li><a href="#">New York</a></li> <li><a href="#">Stockholm</a></li> <li><a href="#">Madrid</a></li> <li><a href="#">Buenos Aires</a></li> <li><a href="#">Tokyo</a></li> </ul> </li> </ul><!-- End .submenu-top --> </li><!-- End .submenu-item--> <li class="submenu-item"> <a href="#" class="submenu-title">Trending destinations</a> <ul class="submenu-bottom"> <li class="submenu-bottom-item"> <figure> <img src="https://picsum.photos/id/1016/640/260"> <figcaption><a href="#">Mountains</a></figcaption> </figure> </li> <li class="submenu-bottom-item"> <figure> <img src="https://picsum.photos/id/1040/640/260"> <figcaption><a href="#">Castles</a></figcaption> </figure> </li> <li class="submenu-bottom-item"> <figure> <img src="https://picsum.photos/id/1039/640/260"> <figcaption><a href="#">Waterfalls</a></figcaption> </figure> </li> <li class="submenu-bottom-item"> <figure> <img src="https://picsum.photos/id/164/640/260"> <figcaption><a href="#">Historical towns</a></figcaption> </figure> </li> </ul> </li><!-- End .submenu-item --> </ul> </li> <li class="menu-item"><a href="#">About</a></li> <li class="menu-item"><a href="#">Blog</a></li> <li class="menu-item"><a href="#">Contact</a></li> </ul> </nav>
In the HTML above, I used Font Awesome icons and pulled images from Lorem Picsum. If you look at the screenshot below, you will immediately understand why we began by creating the template:

The items already line up in the layout we want. Now we only have to do some minor alignment work, then remove the helper colors and borders of the template (unless you’re into the whole brutalist aesthetic?!).
4. Align the Items Inside Each Section
In this step, we won’t touch the HTML, just fix the smaller issues of the layout using CSS. The first thing I see when I look at the image above is that the Holiday packages section in .submenu-top
is too wide compared to the other items. This could get worse if we have even longer descriptions. So, it’s reasonable to add a max-width
value to the flex items in .submenu-top
, for example:
.submenu-top-item { max-width: 33.333%; }
By controlling the width of the flex items, our mega menu looks much nicer:

Now to apply a couple of tiny adjustments to the CSS. We turn each list item of the first and second columns into a separate flex container. This means four tiny flex containers in the Holiday types section and three tiny flex containers in Holiday packages.
We use this solution to force the elements to decently line up next to or below each other in the following way:
- in the Holiday types section, we use
align-items: center;
to show the text exactly at the middle line of the thumbnail image, - in the Holiday packages section, we use
flex-direction: column;
to show each description below the belonging definition (Families, Students, Couples).
In the other sections, we won’t use flexbox for inline alignment, as it can be done with basic CSS rules, namely margin
and width
:
/* Submenu styling */ .submenu .submenu-title { text-decoration: none; font-weight: bold; } .submenu-list, .submenu-bottom { margin-top: 10px; } /* Thumbnail list */ .thumb-list .submenu-list > li { display: flex; align-items: center; } .submenu-thumbnail { margin-right: 10px; } /* Description list */ .desc-list .submenu-list > li { display: flex; flex-direction: column; } .submenu-desc { margin-top: 10px; color: #555; } /* Icon list */ .submenu-icon { width: 32px; } /* Submenu bottom */ .submenu-bottom figcaption { margin-top: 5px; font-weight: bold; }
This is how our aligned mega menu looks the moment:

5. Remove the Template Helpers
Now, everything lines up properly! We now have to remove the template’s helper colors and borders and add the final colors to the links and heading sections. Below, you can find the final CSS without the hover rule:
/* General styling */ * { box-sizing: border-box; margin: 0; padding: 0; font-family: sans-serif; } ul { list-style-type: none; } a { color: white; text-decoration: none; } .submenu-icon { color: #111; } a:hover { text-decoration: underline; } img { max-width: 100%; } .menu { background-color: darkslateblue; } .submenu { border: 1px solid #ccc; } .menu li { padding: 10px; } .menu-icon { color: white; margin-left: 10px; font-size: 14px; } /* Flexbox rules */ .menu { display: flex; align-items: center; text-align: center; width: 90vw; margin: 10px 5vw; height: 60px; position: relative; } .menu-item { flex: 1; } .menu-item > a { line-height: 40px; } .submenu { width: 90vw; position: absolute; top: 60px; left: 0; text-align: left; display: flex; flex-direction: column; } .submenu-item { padding: 15px; } .submenu-top { display: flex; justify-content: space-around; } .submenu-bottom { display: flex; } .submenu-bottom-item { flex: 1; } /* Submenu general styling */ .submenu-top li { padding-left: 0; } .submenu a { color: #111; } .submenu-top-item { max-width: 33.333%; } .submenu .submenu-title { font-weight: bold; color: darkslateblue; } .submenu .submenu-title:hover { text-decoration: none; } .submenu-list, .submenu-bottom { margin-top: 10px; } /* Submenu: Thumbnail list */ .thumb-list .submenu-list > li { display: flex; align-items: center; } .submenu-thumbnail { margin-right: 10px; } /* Submenu: Description list */ .desc-list .submenu-list > li { display: flex; flex-direction: column; } .submenu-desc { margin-top: 10px; color: #555; } /* Submenu: Icon list */ .submenu-icon { width: 32px; } /* Submenu bottom */ .submenu-bottom-title { padding-left: 10px; } .submenu-bottom figcaption { margin-top: 5px; font-weight: bold; } .submenu-bottom a:hover { text-decoration: none; }
The HTML is still the same as in Step 3. However, we’ve also added a down-arrow icon from Font Awesome to the Holidays menu item to indicate the presence of the mega menu (we style it using the .menu-icon
handle in the CSS above).
<li class="menu-item holidays"><a href="#">Holidays</a> <i class="menu-icon fas fa-angle-down"></i> ... </li>
Below, you can see that everything looks as expected:

6. Add the Hover Rule
To make the mega menu drop down on hover, we need to hide it and reveal it only when .menu-item
gets its hover state:
.submenu { display: none; } .menu-item:hover .submenu { display: flex; } .menu-item:hover > a { text-decoration: underline; }
Now, our flexbox mega menu is up and running. In the demo below, you can test it and check out the corresponding HTML and CSS code, too:
Wrapping Up
Although building a mega menu is a complicated task, it’s perfectly doable with flexbox, especially if you first create a template that helps you get the layout right.
You might ask if it’s not easier/better to create a mega menu with CSS grid instead of flexbox, and I say “it depends”. Although CSS grid allows you to create two-dimensional layouts, flexbox comes with out-of-the-box content awareness which is ideal in this scenario.
If you turn each row of the mega menu into a flex container, you can apply different alignment, wrapping, and sizing rules to each. This is the design principle that flexbox-based frameworks such as Bootstrap 4 follow, too. Besides, flexbox still has better support than CSS grid (currently 98.3% of all browsers used globally compared to CSS grid’s 92.03%, however this will change in the future).
You can also make your mega menu responsive using media queries together with the flexbox layout. If you want to know how to go about this, check out my previous tutorial on how to build a responsive navigation bar with flexbox.
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post