Link Search Menu Expand Document

Next.js Blog Tutorial - Part 2

Create blog posts and navigate to them with a dropdown menu.

Starting code: https://replit.com/@buckldav/next-tut-1.

Creating Blog Folder and Files

In the pages/ folder, create a blog/ folder with two files, post1.js and post2.js.

Blog Folders

pages/
  blog/
    post1.js
    post2.js

Creating Posts

Make Post 1 like the below markup. You can choose what text goes between the tags, but be sure to pick the same tags in this example for this tutorial.

pages/blog/post1.js

import Head from "next/head"
import Image from "next/image"

export default function Post1() {
  return (
    <main>
      <Head>
        <title>My Next App | Post 1</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <article>
        <header>
          <h1>Post 1</h1>
          <p>October 14, 2021</p>
          <p>by David Buckley</p>
        </header>

        <div>
          <p>This is some content for my first post.</p>
        </div>
      </article>
    </main>
  )
}

Once Post 1 is complete, copy + paste the markup and modify text for Post 2. You can navigate to /blog/post1 and /blog/post2 in your browser to see them.

Post Page

Interjection - ID selector

So far, we have been mostly using element and class selectors in CSS. Now it’s time to introduce the ID selector. ID’s can be used to identify and select a single element. Here’s an example.

<style>
  #hello {
    font-size: 60px;
    color: green;
    font-weight: bold;
    font-style: italic;
  }
</style>

<p id="hello">Hello</p>
<p>Normal Text</p>

Hello Page

Writing inline style for this single element would look horrific and would be hard to maintain, so using an ID in this case is superior.

<p
  style="font-size: 60px; color: green; font-weight: bold; font-style: italic;"
>
  Hello
</p>

In our project, we are using an ID as part of a dropdown menu to list our blog posts in the navigation. The ID will allow us to identify the element containing the dropdown in our JavaScript and then show and hide it as appropriate.

Creating a Dropdown in our Navigation

We could list all of our blog posts horizontally in our navigation, but this would not scale well. Instead, we will make a dropdown menu where all blog posts are listed. Here’s what the final product will look like:

Nav Hover Result

components/Nav.js

Remember how components can have interactivity (written in JavaScript)? We are going to add a dropdown() function that runs anytime the dropdown menu is toggled (shown/hidden).

NOTE: This way of selecting the element using getElementById in the dropdown() function is not “Reactive”, meaning that there are more idiomatic ways to do this in a React or Next project (check out useState in React’s docs). However, this way of toggling a CSS class will work in any frontend project, so that’s why it is presented.

This is what your entire Nav.js file should look like:

import Link from "next/link"
import styles from "../styles/components/Nav.module.css"

export default function Nav() {
  function dropdown() {
    document.getElementById("blogDropdown").classList.toggle(styles.show)
  }

  return (
    <nav className={styles.nav}>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>

      <div className={styles.dropdown}>
        <button className={styles.dropBtn} onClick={dropdown}>
          Blog
        </button>
        <div id="blogDropdown" className={styles.dropContent}>
          <Link href="/blog/post1">Post 1</Link>
          <Link href="/blog/post2">Post 2</Link>
        </div>
      </div>
    </nav>
  )
}

styles/components/Nav.module.css

We need to add some styles to our CSS module in order to make this work. Add the below code after the existing styles in our CSS file.

/* The container <div> - needed to position the dropdown content */
.dropdown {
  position: relative;
  display: inline-block;
}

/* Dropdown Button */
.dropBtn {
  display: inline-block;
  color: #0070f3;
  padding: 1em;
  background-color: transparent;
  border: none;
  font-size: inherit;
  font-family: inherit;
  cursor: pointer;
}

/* Dropdown Content (Hidden by Default) */
.dropContent {
  display: none;
  position: absolute;
  background-color: #f1f1f1;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
}

/* Links inside the dropdown */
.dropContent a {
  display: block;
}

/* Show the dropdown menu (use JS to add this class to the .dropContent container when the user clicks on the dropdown button) */
.show {
  display: block;
}

CSS Concepts

CSS Display

We’ve already talked about CSS Display if you are in App & Web Development class at Merit. display: block means the element takes up the full width of its parent. display: inline-block means the element is rendered inline with its adjacent elements, but can have Box Model styling applied to it. Note that .dropContent has display: none;, meaning that the element is not rendered. If the element has the classes dropContent and show, the display: block rule on the show class overrides the display: none, thus rendering the dropdown menu on the screen. That’s why the JavaScript function dropdown() in our Nav component toggles the show class.

CSS Position

By default, elements have position: static, meaning they are rendered one after another. position: relative and position: absolute can be used together to render elements on top of the static flow (like our dropdown menu).

<parent style="position: relative">
  <child style="position: absolute" />
</parent>

If these two position rules are used like in the example above, the child element will be rendered below the parent element but on top of the rest of the page.

More Dropdown Examples

Pseudo Selectors

To finish things off, let’s add some pseudo selectors to our navigation CSS. Pseudo selectors allow for us to change the style depending on the state of the element.

The :hover pseudo selector is triggered whenever the mouse hovers the element.

.nav a:hover,
.nav button:hover {
  color: #0053b3;
}

Nav Hover Result


The :focus and :active pseudo selectors are for elements that have been clicked on (like our dropdown button).

.nav a:hover,
.nav button:hover,
.nav button:focus,
.nav button:active {
  color: #0053b3;
}

Nav Focus Result

More: w3schools CSS Pseudo Classes

Summary

In this part, we created some blog post pages, add our first bit of JavaScript logic to a component, and learned some CSS tricks to help enhance our navigation.

Finished code: https://replit.com/@buckldav/next-tut-2

Extra: Make the blog dropdown menu hide on window click.

This covers a concept outside of the scope of this tutorial page.

// components/Nav.js
import { useEffect } from "react"
// other imports

export default function Nav() {
  // dropdown function is above this
  useEffect(() => {
    window.onclick = function (e) {
      if (!e.target.classList.contains(styles.dropBtn))
        document.getElementById("blogDropdown").classList.remove(styles.show)
    }
  }, [])
  // return statement is below this
}
Learning Targets

Standard 2.2

  • File structure and naming.

Standard 3.3

  • Implement an id selector to modify a single element on the page.
  • Implement selectors a:link, a:visited, a:active, a:hover.

Standard 3.4

  • Use absolute and static to position elements.

Standard 4.2

  • Input Controls: Dropdown Lists.
  • Navigational Components.