React: Create a Responsive Navbar from Scratch
I wanted to create a responsive react navbar from scratch. I’m going to do this without using any CSS libraries just because I wanted to see if I can… I also wanted to refresh my memory on React. So here we go.
honeypot email: objekt@objekt.click
I hate wordy tutorials, so let’s jump right in. The final product will look like this:
Install node (>= 6)
brew install node
Create a react app
npx create-react-app navbar
cd navbar
npm start
What does all this mean?
- To use react, you need node hence
brew install node
- Use
create-react-app
to create react boilerplate code cd
into the directorynpm start
the development server
Install fontawesome for react
npm i --save @fortawesome/fontawesome
npm i --save@fortawesome/fontawesome-free-solid
npm i --save @fortawesome/react-fontawesome
Your package.json
would simply look like this:
{
"name": "navbar",
"version": "0.1.0",
"private": true,
"homepage": "./",
"dependencies": {
"@fortawesome/fontawesome": "^1.1.8",
"@fortawesome/fontawesome-free-solid": "^5.0.13",
"@fortawesome/react-fontawesome": "0.0.19",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-scripts": "1.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
Now let’s go ahead and create the correct structure and files under source. Namely topMenu
, item
, lead
, and their relative index.js
and index.css
. Our goal here is to be as general as possible to create contained reusable objects.
We’ll start with topMenu > index.js
import React, { Component } from 'react';
import FontAwesomeIcon from '@fortawesome/react-fontawesome'
import faBars from '@fortawesome/fontawesome-free-solid/faBars'
import Item from './item'
import Lead from './lead'
import './index.css'
class TopMenu extends Component {
constructor(props) {
super(props)
this.state = {
menu_class: '',
}
}
setToggleTopMenuClass = () => {
if (this.state.menu_class === '') {
this.setState({
menu_class: 'toggled',
})
} else {
this.setState({
menu_class: '',
})
}
}
render = () => {
let top_menu_class = `top-menu ${this.state.menu_class}`
return (
<div>
<div className={top_menu_class} >
<Lead text="This Is Your Title!" />
<div className='left'>
<Item text='Left1'/>
<Item text='Left2'/>
</div>
<div className='right'>
<Item text='Right1' />
<Item text='Right2' />
</div>
<FontAwesomeIcon icon={faBars} className='top-menu-icon' onClick={this.setToggleTopMenuClass}/>
<div className='clear-fix' />
</div>
</div>
)
}
}
export default TopMenu;
So what’s happening?
- Straightforward imports of
fontawesome
,item
andlead
constructor
function that sets thestate
setToggleTopMenuClass
that sets the state totoggled
or blank which then is used inindex.css
to toggle the menurender
function renders the component, obviously.
Now onto topMenu > index.css
.top-menu {
background-color: grey;
padding: 20px 50px 20px 50px;
user-select: none;
}
.top-menu > .right {
float: right;
}
.top-menu > .right > * {
margin-left: 5px;
}
.top-menu > .left {
float: left;
}
.top-menu > .left > * {
margin-right: 5px;
}
.top-menu > .left > *, .top-menu > .right > * {
padding: 11.4px;
display: inline-block;
text-align: center;
min-width: 50px;
}
.top-menu *:hover{
cursor: pointer;
}
.clear-fix {
clear: both;
}
.top-menu .top-menu-icon {
padding: 10px;
position: absolute;
top: 0;
right: 0;
display: none;
}
@media screen and (max-width: 600px) {
.top-menu {
padding: 40px 20px 20px 20px;
max-height: 0;
overflow: hidden;
}
.top-menu > .left, .top-menu > .right {
display: none;
}
.top-menu-icon {
display: block !important;
}
}
@media screen and (max-width: 600px) {
.top-menu.toggled {
padding: 60px 0 0 0;
max-height: 1500px;
transition: max-height 1s;
}
.top-menu.toggled > .left {
border-bottom: 1px solid black;
margin: 15px 0 0 0 ;
}
.top-menu.toggled *:not(.top-menu-lead):not(.top-menu-icon) {
float: none;
display: block !important;
text-align: left;
margin: 0;
}
}
What’s happening here:
- We’re setting the topMenu and children’s CSS properties
- We’re setting properties for the toggled and un-toggled states when
max-width
is 600 pixels
item.js
and item.css
are super simple:
import React, { Component } from 'react';
import './index.css'
class Item extends Component {
constructor(props) {
super(props)
this.text = props.text
}
render() {
return (
<div className='top-menu-item'>
{this.text}
</div>
)
}
}
export default Item;
.top-menu-item:hover {
background-color: white;
}
The code:
- Sets the item text that gets passed down from
topMenu > index.js
component namelyLeft1
,Left2
, etc - Sets the
background-color
to white on hover
lead.js
and lead.css
are pretty simple too:
import './index.css'
class Lead extends Component {
constructor(props) {
super(props)
this.text = props.text
}
render() {
return (
<div className='top-menu-lead'>
{this.text}
</div>
)
}
}
export default Lead;
.top-menu-lead {
padding: 10px;
background-color: #000000;
color: white;
display: inline-block;
float: left;
font-size: 18px;
}
@media screen and (max-width: 600px) {
.top-menu-lead {
position: absolute;
top: 10px;
left: 10px;
float: none;
}
}
The code:
- Sets the lead text that gets passed down from
topMenu > index.js
component namelyThis Is Your Title!
- tweaks the size and shape of the lead via CSS so that it would stand out from the rest of the menu
And… done! Simple, eh?
We can add as many menu items as we want, by simply adding a new <Item text='whatever'/>
to the TopMenu
component under either left
or right
classes. The Item
component is completely independent of TopMenu
.