Creating responsive navbars with background images in GatsbyJS using gatsby-image
Tags: gatsby, react, graphql, javascript
Published: 2019-10-03 23:11:21

![gatsby logo](https://thepracticaldev.s3.amazonaws.com/i/rdckude3n23s9gnbb2mg.png)

Recently I decided to redo [my personal static page](http://fidel.engineer) with React, while searching the web for a way to generate statics React sites I found out about [Gatsbyjs](https://www.gatsbyjs.org/), and after a couple of hours of reading about it, my two days journey of learning the hard way started.

![homepage horizontal](https://thepracticaldev.s3.amazonaws.com/i/mucghi0l0bafj4e17ssy.png)

The main challenge I didn't know I was going to face, was porting the responsive navbar that I created for my site with simple CSS (flexbox, media queries, and background images).

The position of the navbar in my site changes from vertical to horizontal depending on the screen size of the browser, and, depending on this position, the background image changes.

![homepage vertical](https://thepracticaldev.s3.amazonaws.com/i/ix40tmtj3ta5ker1sf69.png)

While reading the Gatsby official docs and tutorial, I found out about [gatsby-image](https://www.gatsbyjs.org/packages/gatsby-image) and decided to use it.

>gatsby-image is a React component specially designed to work seamlessly with Gatsby’s GraphQL queries. It combines Gatsby’s native image processing capabilities with advanced image loading techniques to easily and completely optimize image loading for your sites.

By using gatsby-image you can optimize image loading, it automatically creates different images to use depending on display size and also applies progressive image loading (blur up), the problem for me was, that it doesn’t work with background images.

There is another package called gatsby-background-image, but, since I already had an idea on how to make it work, I decided not to use it.

The way I decided to implement it was simple, first create an absolute positioned container to display the image and then a second absolute positioned container for the links stacked on top.

```html
<nav>
  <div>
  <!-- gatsby-image here -->
  </div>
  <div>
  <!-- navbar links here -->
  </div>
</nav>
```

## Creating the Project Structure

I am going to assume that you already have [Gatsby installed](https://www.gatsbyjs.org/docs/quick-start/) and that you already created a project using the command `gatsby new <project-name>`.

In my case, the folder structure of my project looks like this (not displaying the *node_modules* and *public* folders).

![project structure](https://thepracticaldev.s3.amazonaws.com/i/0se41gwarm8o3fgo5uz8.png)

Inside the *src/components* folder you can see 3 files:

* **layout.css** a vary basic CSS reset with the following content:

```css
html {
  font-family: sans-serif;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}
body {
  margin: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
```

* **layout.module.css**. A [CSS Module](https://www.gatsbyjs.org/docs/css-modules/) to [avoid name collision](https://philipwalton.com/articles/side-effects-in-css/). Here we are going to write all of our CSS code for our **Layout Component**.

* **layout.js** our **Layout Component**.

Inside the *src/images* we have our two background images for when the navbar is either horizontal or vertically positioned, and inside the *src/pages* we have the 3 [Page Components](https://www.gatsbyjs.org/docs/building-with-components/) that are going to be linked at in the navbar. This is the code inside the **Page Components**.

```javascript
import React from "react"

import Layout from "../components/layout"

const IndexPage = () => (
  <Layout headerText="Index Page Header text.">
    <p>
      "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua.
    </p>
  </Layout>
)

export default IndexPage

```

## Creating our layout Component

Because all the pages in my site are structured the same way, I decided to create a [Layout Component](https://www.gatsbyjs.org/docs/layout-components/), and use it across all the pages.

This **Layout Component** will have the responsive navbar, and the `graphql` queries.

```javascript
import React from "react"
import { Link } from "gatsby"
import styles from "./layout.module.css"
import "./layout.css"

class Layout extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
      <div id={styles.layout}>
        <nav id={styles.navbar}>
          <div id={styles.navbarBackground}>
            {/* Background Image goes here */}
          </div>
          <div id={styles.navbarLinkContainer}>
            <Link className={styles.navLink} to="/">HOME</Link>
            <Link className={styles.navLink} to="/about/">ABOUT</Link>
            <Link className={styles.navLink} to="/contact/">CONTACT</Link>
          </div>
        </nav>
        <main id={styles.main}>
          <header>
            <h1>{this.props.headerText}</h1>
          </header>
          <div>{this.props.children}</div>
        </main>
      </div>
    )
  }
}
```

## Using CSS media queries

In order to change the position of the navbar we need to use [CSS media queries](https://www.w3schools.com/css/css3_mediaqueries.asp). For my page, I decided to use a screen width of 580 pixels as the breakpoint in the query.

```css
#layout {
  min-height: 100%;
  min-width: 320px;
}
#navbar,
#navbar-background,
#navbar-link-container {
  height: 60px;
}
#navbar {
  position: fixed;
  z-index: 999;
  top: 0px;
  width: 100%;
  background-color: #eee;
}
#navbar-background {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  overflow: hidden;
}
#navbar-link-container {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  flex-flow: row nowrap;
  text-align: center;
}
.nav-link {
  font-size: 20px;
  display: block;
  background-color: rgba(0, 0, 0, 0.1);
  text-decoration: none;
  color: #333;
  font-weight: bold;
  padding: 20px 4px 0px 2px;
}
.nav-link:hover {
  background-color: rgba(0, 0, 0, 0.4);
  color: #fff;
}
#main {
  margin-top: 75px;
}

@media screen and (min-width: 580px) {
  /* if width more than 580px */
  #layout {
    display: flex;
    flex-flow: row nowrap;
    height: 100vh;
  }
  #navbar,
  #navbar-background,
  #navbar-link-container {
    width: 170px;
    height: 100vh;
  }
  #navbar {
    flex-grow: 0;
    flex-shrink: 0;
    position: static;
    background-color: rgba(0, 0, 0, 0.1);
  }
  #navbar-link-container {
    flex-flow: column nowrap;
  }
  #main {
    margin-top: 0px;
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: 100px;
  }
}
```

## Detecting the navbar position inside React

Because we are using **CSS queries** to change the position of our navbar, and the image we are going to be displaying as a background changes depending on this position, we need to find a way to detect the navbar position from inside **React** and display the correct image.

This is the main reason why I implemented the **Layout Component** as a class component and not a function component. We need to have the *sidebar position* as a state inside the **Layout Component** and re-render it when it changes.

The way we are going to do this is by first, detect the position of the navbar on the first rendering, listening for resize events and detect when our navbar changes position based on our breakpoint (width < 580px).

```javascript
//.....//
class Layout extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      navbarPosition: this.getPosition(),
    }
  }

  getPosition = () => {
    // This conditional is to avoid errors during build, check
    // https://www.gatsbyjs.org/docs/debugging-html-builds/
    if (typeof window !== "undefined") {
      return window.innerWidth < 580 ? "horizontal" : "vertical"
    }
  }
  componentDidMount() {
    window.addEventListener("resize", this.updateSize)
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.updateSize)
  }
  updateSize = () => {
    let position = this.getPosition()
    if (position !== this.state.navbarPosition) {
      // Changing the state will cause React to re-render, in
      // this case we are only changing the state when the
      // navbar changes position
      this.setState({ navbarPosition: position })
    }
  }
  render() {
    //.....//
  }
}
```

Now that we have the basic structure, we need to use `gatsby-image` with `graphql` to display the images in our `#navbar-background` container.

## Using graphql with gatsby-image

In order to use `gatsby-image` we need to query for the image files with `graphql`, if you don't know how `graphql` queries work on gatsby, I highly recommend to first read about it in [here](https://www.gatsbyjs.org/docs/graphql-concepts/).

In my case the `graphql` query looks like this:

```javascript
{
  navbarBgVer: file(relativePath: { eq: "navbar-ver.jpeg" }
  {
  ¦ childImageSharp {
  ¦ ¦ fluid(maxHeight: 1200) {
  ¦ ¦ ¦ ...GatsbyImageSharpFluid
  ¦ ¦ }
  ¦ }
  }
  navbarBgHor: file(relativePath: { eq: "navbar-hor.jpeg" }
  {
  ¦ childImageSharp {
  ¦ ¦ fluid(maxWidth: 2000) {
  ¦ ¦ ¦ ...GatsbyImageSharpFluid
  ¦ ¦ }
  ¦ }
  }
}

```

There are 3 ways of using `graphql` with **Gatsby**, and which one to use, depends on several factors:

* [Page queries](https://www.gatsbyjs.org/docs/page-query/), that are only to be used in gatsby page components.
* [useStaticQuery Hook](https://www.gatsbyjs.org/docs/use-static-query/)
* and [StaticQuery](https://www.gatsbyjs.org/docs/page-query/), which can be used in any component.

Because we are going to be making the `graphql` query outside a **Page Component**, we have to either use **StaticQuery** or **useStaticQuery**. For this case I decided to go with **StaticQuery** because is what am most comfortable with right now, **useStaticQuery** is a **React Hook** and even though we should be using them (they seem to be the way [most developers](https://www.robinwieruch.de/react-hooks-migration) will be using **React** from now on), I haven't yet learned them, so, rewriting my site to use **React Hooks** might be a good project for the future.

Adding the `<StaticQuery />` with the `graphql` query to our **layout.js** component, our new `render()` function will look like this.

```javascript
render() {
  const navbarPosition = this.state.navbarPosition
  return (
    <StaticQuery
      query={graphql`
        {
          navbarBgVer: file(relativePath: { eq: "navbar-ver.jpeg" }) {
            childImageSharp {
              fluid(maxHeight: 1200) {
                ...GatsbyImageSharpFluid
              }
            }
          }
          navbarBgHor: file(relativePath: { eq: "navbar-hor.jpeg" }) {
            childImageSharp {
              fluid(maxWidth: 2000) {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      `}
      render={data => (
        <div id={styles.layout}>
          <nav id={styles.navbar}>
            <div id={styles.navbarBackground}>
              {/* gatsby-image components*/}
              {navbarPosition === "vertical" ? (
                <Img
                  style={{ height: "100%" }}
                  fluid={data.navbarBgVer.childImageSharp.fluid}
                />
              ) : (
                <Img
                  style={{ minHeight: "60px" }}
                  fluid={data.navbarBgHor.childImageSharp.fluid}
                />
              )}
            </div>
            <div id={styles.navbarLinkContainer}>
              <Link className={styles.navLink} to={"/"}>
                HOME
              </Link>
              <Link className={styles.navLink} to={"/about/"}>
                ABOUT
              </Link>
              <Link className={styles.navLink} to={"/contact/"}>
                CONTACT
              </Link>
            </div>
          </nav>
          <main id={styles.main}>
            <header id={styles.mainHeader}>
              <h1>{this.props.headerText}</h1>
            </header>
            <div id={styles.content}>{this.props.children}</div>
          </main>
        </div>
      )}
    />
  )
}
```
As you can see we are rendering our entire **Layout Component** inside the `<StaticQuery />` element acting as a wrapper. This `<StaticQuery />` element takes two attributes

* The first one is `query`. Inside of this attribute we are doing our `graphql` query.

* The second one is `render`. This one takes a function and passes onto it the `graphql` data as an argument, so we can use it on our **Layout Component**.

As you can see, inside our `#navbar-background` container we are conditionally rendering our background image depending on the navbar position.

```javascript
<div id={styles.navbarBackground}>
  {/* navbar background image goes here */}
  {navbarPosition === "vertical" ? (
    <Img
      style={{ height: "100%" }}
      fluid={data.navbarBgVer.childImageSharp.fluid}
    />
  ) : (
    <Img
      style={{ minHeight: "60px" }}
      fluid={data.navbarBgHor.childImageSharp.fluid}
    />
  )}
</div>
```

Now, our final **layout.js** component will looks like this.

```javascript
/**
 * Layout component for all the pages of the site
 */
import React from "react"
import { Link } from "gatsby"
import { StaticQuery, graphql } from "gatsby"
import styles from "./layout.module.css"
import "./layout.css"
import Img from "gatsby-image"

class Layout extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      navbarPosition: this.getPosition(),
    }
  }

  getPosition = () => {
    // This conditional is to avoid errors during build, check
    // https://www.gatsbyjs.org/docs/debugging-html-builds/
    if (typeof window !== "undefined") {
      return window.innerWidth < 580 ? "horizontal" : "vertical"
    }
  }
  componentDidMount() {
    window.addEventListener("resize", this.updateSize)
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.updateSize)
  }
  updateSize = () => {
    let position = this.getPosition()
    if (position !== this.state.navbarPosition) {
      // Changing the state will cause a re-render of the page, do in this case
      // we are only changing the state when the navbar changes position
      this.setState({ navbarPosition: position })
    }
  }

  render() {
    const navbarPosition = this.state.navbarPosition
    return (
      <StaticQuery
        query={graphql`
          {
            navbarBgVer: file(relativePath: { eq: "navbar-ver.jpeg" }) {
              childImageSharp {
                fluid(maxHeight: 1200) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
            navbarBgHor: file(relativePath: { eq: "navbar-hor.jpeg" }) {
              childImageSharp {
                fluid(maxWidth: 2000) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
        `}
        render={data => (
          <div id={styles.layout}>
            <nav id={styles.navbar}>
              <div id={styles.navbarBackground}>
                {/* navbar background image goes here */}
                {navbarPosition === "vertical" ? (
                  <Img
                    style={{ height: "100%" }}
                    fluid={data.navbarBgVer.childImageSharp.fluid}
                  />
                ) : (
                  <Img
                    style={{ minHeight: "60px", width: "110%" }}
                    fluid={data.navbarBgHor.childImageSharp.fluid}
                  />
                )}
              </div>
              <div id={styles.navbarLinkContainer}>
                <Link className={styles.navLink} to={"/"}>
                  HOME
                </Link>
                <Link className={styles.navLink} to={"/about/"}>
                  ABOUT
                </Link>
                <Link className={styles.navLink} to={"/contact/"}>
                  CONTACT
                </Link>
              </div>
            </nav>
            <main id={styles.main}>
              <header id={styles.mainHeader}>
                <h1>{this.props.headerText}</h1>
              </header>
              <div id={styles.content}>{this.props.children}</div>
            </main>
          </div>
        )}
      />
    )
  }
}

export default Layout
```

## My impressions about gatsby.

I ended this small personal project very satisfied with **Gatsby**, I was looking for a way to port my static personal page to **React**, and **Gatsby** not only allowed me to do this, but I also learned about `gatsby-image`, which I thinks is a great tool for optimizing image rendering. I think that **Gatsby** will become one of my go-to frameworks for future projects.

I hope this article helps you in your journey, thanks for reading!.