Qt's Home

The Story of a french guy discovering the world

ReactJS - How to Make a Simple Navbar

| Comments

Before we start

I’ve got a coding challenge a few weeks ago about how to make a navbar out of pure JS/CSS, no library. I won’t spoil it, especially for the next people doing it, but I thought the idea interesting so I decided to show how one could do one in ReactJS.

So, this is a guide about how to make a simple dropdown navbar from some backend data and simple React/CSS.

To understand you’ll need :

How-to

What we have

We have a basic html page with the React files (from a CDN here) :

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
  <title>My page</title>
  <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/react/0.11.0/react.min.js"></script>
  <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/react/0.11.0/JSXTransformer.js"></script>

</head>
<body>
<nav></nav>
</body>
</html>

and the data we get from our backend to populate this navbar. To do so I’ll format them like this :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[
 {
  "text": "THE TEXT OF YOUR LINK",
  "url": "http://THE.URL"
 },
 {
  "text": "THE TEXT OF YOUR LINK",
  "url": "http://THE.URL",
  "submenu": [
      {
      "text": "THE TEXT OF YOUR LINK",
      "url": "http://THE.URL"
      }
  ]
 }
]

The submenu is not needed and we’ll build the navbar accordingly.

Our React components

We have : a Navbar container, Navbar items containing a link and a submenu if possible, and Navbar links.

So, from the smallest to the biggest

navbarLink.jsx

1
2
3
4
5
6
7
8
/** @jsx React.DOM */
var NavBarLink = React.createClass({
  render: function() {
    return (
      <a href={this.props.url}>{this.props.text}</a>
    );
  }
})

We render a simple link, separately. This is easier for events handlers, and … OOJS !

navbarItem.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/** @jsx React.DOM */
var NavBarItem = React.createClass({
  generateLink: function(){
  //Right now we don't need our class but what if we wanted to change the text, add an arrow or something? 
    //Single responsibility principles tell us that it our "Item" should not handle this.
    return <NavBarLink url={this.props.url} text={this.props.text} />;
  },
  generateSubmenu: function(){
  //We generate a simple Navbar (the parent). 
  //Spoilers: it takes items as its argument. 
    return <NavBar items={this.props.submenu} />
  },
  generateContent: function(){
    var content = [this.generateLink()];
    if(this.props.submenu){
      //If there is a submenu in our data for this item
      //We add a generated Submenu to our content
      content.push(this.generateSubmenu());
    }
    return content;
  },
  render: function() {
    var content = this.generateContent();
    return (
      <li>
        {content}
      </li>
    );
  }
})

And finally our NavBar main container :

navbar.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** @jsx React.DOM */
var NavBar = React.createClass({
  generateItem: function(item){
    return <NavBarItem text={item.text} url={item.url} submenu={item.submenu} />
  },
  render: function() {
    var items = this.props.items.map(this.generateItem);
    return (
      <ul className="menu">
      {items}
      </ul>
    );
  }
})

To sum it up, our Navbar generates NavbarItems from the array of items we gave it. Each NavbarItem contains a NavbarLink + an optional NavBar, which would simply be the submenu we want.

All in all, it generates an html that looks like this :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<nav>
  <ul class="menu" data-reactid=".0">
      <li data-reactid=".0.0"><a href="#" data-reactid=".0.0.0">Link 1</a></li>
      <li data-reactid=".0.1"><a href="#" data-reactid=".0.1.0">Link 2</a></li>
      <li data-reactid=".0.2">
          <a href="#" data-reactid=".0.2.0">Link 3</a>
          <ul class="menu" data-reactid=".0.2.1">
              <li data-reactid=".0.2.1.0">
                  <a href="#" data-reactid=".0.2.1.0.0">Sublink 1▼</a>
                  <ul class="menu" data-reactid=".0.2.1.0.1">
                      <li data-reactid=".0.2.1.0.1.0"><a href="#" data-reactid=".0.2.1.0.1.0.0">SubSublink 1</a></li>
                  </ul>
              </li>
              <li data-reactid=".0.2.1.1">
                  <a href="#" data-reactid=".0.2.1.1.0">Sublink 2▼</a>
                  <ul class="menu" data-reactid=".0.2.1.1.1">
                      <li data-reactid=".0.2.1.1.1.0"><a href="#" data-reactid=".0.2.1.1.1.0.0">SubSublink 2</a></li>
                  </ul>
              </li>
          </ul>
      </li>
  </ul>
</nav>

The CSS

Now, we need to style that because it honestly looks like… bad things (PG-rated tutorials FTW). Nothing fancy in here, you’re the one styling it, but let me show you what you’ll need :

style.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
.menu {
  list-style: none;
  /* Important for sizing problems when you add/remove the submenus 
   Make it use 100% of the size of your container */
  width: 100%;
  /* This prevents a lot of problems with the submenus. 
   UL have basic padding, keep that in mind */
  padding: 0;
}

.menu li {
  /* I wanted mine horizontal. Feel free to change it to what you want */
  display:inline-block;
  /* Float the items to the left, to make sure the disposition does not change when it shows the subitems */
  float:left;
  /* The -1px on top and right make it so that our borders end up above one another
     Not needed it you don't have borders... */
  margin:-1px -1px 0 0;
}

.menu li a {
  display:block;


  /* My basic styling. I'm just giving them a black text, with a black border, and center the text in the box. */
  text-decoration:none;
  border: 1px solid black;
  vertical-align: middle;
  text-align: center;

  /* Fixed size is also just me. 
     Not fixing it just make huge boxes when you have submenus */
  width:100px;
  height:50px;
  line-height: 50px;
}

.menu li > ul {
/****** THE INTERESTING PART ******/
/* We hide the submenus by putting them somewhere on the left, with no opacity. */
  left:-9999px;
  opacity: 0;

/* We make transitions on opacity */
  -webkit-transiton: opacity 0.3s;
  -moz-transition: opacity 0.3s;
  -ms-transition: opacity 0.3s;
  -o-transition: opacity 0.3s;
  transition: opacity 0.3s;
}

.menu li:hover > ul {
/* On hover, we get it back to its basic spot
  left:0;
/* And change the opacity to a fully visible one */
  opacity: 1;
};

The result on a beautiful gist

Here is the link on Github. Feel free to download/tweak and comment it.

Next parts

I plan on giving you the version with the dropdowns triggered by a click instead of a hover. This is coming in a few days, the code is almost ready already ;)

Feel free to let me know if I missed/misexplained anything here or by mail at quentin@devauchelle.eu

Comments