multimedia blog

We are working hard 👷🏼‍♂️ 🏗 🚜 to continually add more.
Please email if you have requests or questions 🙋.
Contact 👽 🛸 info below.
📬

create and deploy app

Goals. Build the foundation for your project environment, architecture, and topic.

Setup your developer environments so that it is easy and fast to develop and push live to production. Configure VS Code, git, GitHub, Vercel, and your DNS provider.

Add the client-side and server-side architecture and boilerplate code using the NextJS quickstart.

nextjs-quickstart

Make simple but impactful architecture, layout, and style modifications. For example.

  • Create category subfolders in the content folder
  • Write functions to display posts organized by category on the hompage.
  • Create NextJS pages, routes, static functions, and more

Use developer tools like the terminal and console, see example development and code flow, and practice the basics of git, GitHub, and the command-line.

terminal-vs-code-nextjs

Build a responsive navbar, integrate Google login, and publish your application to a custom domain with Google Analytics.

google-login-authentication

Start thinking about a niche to target with your features and content. Test your website's performance to make sure it is lightning fast and optimized for SEO from the start.

Get started below.

Pick a Niche

Pick a topic for your application. It can be anything. Like something within personal finance, fitness, camping, software, or whatever you want.

You are going to build your application around this topic.

It will impact virtually everything about your application. For instance, it will significantly impact the architecture, forms, and functionality that you build. Of course, it also affects SEO because your headings, images, logos, and other content will target your topic and help you grow your user base.

It is important to have an idea by the time you start doing certain engineering work, like creating folders for categories and adding a custom domain.

Therefore, spend some time thinking about and researching what your application is going to be all about. Look at existing products online and examine what kind of features you like and want to build.

Here are a few tips to choose a topic.

  • Something in your daily life that you have experience with
  • Solving a problem that you actually have or have a solution for
  • Something you have credibility with or are an authority on
  • An existing passion or professsion of yours
  • A prospective passion or professsion of yours
  • Do not worry about being original or unique . . . yet
  • Be specific instead of general
  • Focus on a topic for which you can build tools instead of just text, images, and videos

For now, get the wheels turning. As you go through this course, you gradually build out your topic. So although you are just revving the engine for now, be ready soon to commit to your niche. In a few headings from now, you pick three categories within your topic and create folders for your content.

Visit your profile or sign in to automatically to create a profile and enter your niche in your user profile.

Create Folder and Repo

You need somewhere to write and edit your code. You also need somewhere to store your code for safekeeping and sharing. Visual Studio Code, Git, and GitHub solve those problems.

VS Code is a free and very popular source code editor. Download Visual Studio Code.

Feel free to use whicher code editor you want. This course uses VS Code.

Open VS Code and install an extension named Prettier Code Formatter. Prettier autoformats your code and highlights syntax problems.

The computer cares mostly about syntax and not formatting when reading your code, but Prettier formatting helps you develop code because your teammates and the future you rely upon good formatting to understand and maintain the code.

Click on the extensions icon in the left sidebar of VS Code and search for Prettier. Install it.

prettier-extensions

Next, click on the gear in the bottom left of VS Code and choose Settings.

prettier-settings

Search the settings using the word formatter to set the default formatter to Prettier and to select Format on Save.

prettier-format-on-save

Install Git for version control. As you add new features to your applications, git allows you to save the new version without overwriting the old ones. It saves new versions of your code locally on your computer.

Using git is different than doing File -> Save because git is doing much more than just saving. It is saving versions using version control so that you can see snapshots of your work as you develop it over time.

Create a new folder somewhere on your computer. Name it multimedia-blog or whatever you want. If you already have an idea for your web application, name the folder something related to that.

Open the folder in VS Code. At this point, your folder should be empty.

Open the terminal in VS Code by selecting the View dropdown menu at the top of your computer and then select Terminal. You should see a terminal session open in VS Code.

vs-code-terminal

Point the terminal to your project folder using the following terminal command to change directories.

cd <filepath_for_your_project_folder>
Other than the git commands, the terminal commands here are for a Mac and might not apply to Windows. Soon we will add Windows commands but for now please look them up if you use Windows.

An alternative is to type in your terminal cd followed by a space . Then, drag to the terminal the folder where you want your project folder to live. You should see after the cd a filepath for your folder. Hit enter.

Check the git situation for your new folder.

With your terminal pointing to your project folder, type git status and hit enter.

You should see a list of filepaths, which means you need to initiate a local git repository for your project.

../../../../../../.CFUserTextEncoding
../../../../../../.DS_Store
../../../../../../.Trash/
../../../../../../.cups/
../../../../../../.local/
../../../../../../.netrc
../../../../../../.npm/
../../../../../../.ssh/
../../../../../../Applications/
../../../../../../Desktop/
../../../../../
../../../../../../Downloads/
../../../../../../Library/
../../../../../../Movies/
../../../../../../Music/
../../../../../../Pictures/
../../../../../../Public/

When you initiate a local git repository, it creates a hidden file in your project folder called .git.

List all contents of your project folder in the terminal, including the hidden folders and files. Look for the absence of a .git folder.

ls -al
The . means that it is hidden in your folder.

Initiate a local git repository in your project folder.

Type in your terminal git init and hit enter.

Your terminal should say that you initialized an empty git repository.

Initialized empty Git repository in /Users/jonathangrossman/Documents/Developer/ITC/Work/git/github_exercises/.git/
Only do it once. You should generally avoid having multiple git histories in your projects.

Do two things to confirm.

First, list all the contents in your folder to see the .git file now exists.

You should see a .git file listed in the terminal.

total 16
drwxr-xr-x   4 computer_user  staff   128 Sep 30 14:07 .
drwxr-xr-x   4 computer_user  staff   128 Jun 21 15:35 ..
drwxr-xr-x  12 computer_user  staff   384 Sep 30 14:49 .git

Second, check that git is tracking your folder. Enter the terminal command git status.

git status

It should print out something like the following.

On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        01_getting_started.md

nothing added to commit but untracked files present (use "git add" to track)

Before initiating git, the git status command returned a list of filepaths. Now the terminal output is different. It confirms you successfully initiated a local git repository.

The next step is to add a file to your project.

In your project folder, create a file named index.js. Type something in it. Whatever you want.

Save a copy of your project folder to the local git history. It does not happen automatically like in Google Docs or Microsoft Word.

First, move your files from your project folder to the git staging area using git add -A.

git add -A
Nothing should happen in the terminal after you hit enter. To check that it worked, enter git status again.

The staging does not actually save the changes to your git history. You need to commit for the changes to save to the local git repository.

Second, use git commit -m "a short descriptive message about the changes" to save the files to your local git repository.

The terminal should return a message like the following.

[master (root-commit) 1d49d54] first commit
 1 file changed, 60 insertions(+)
 create mode 100644 01_getting_started.md

You successfully saved your project folder to a local git repository.

Connect the local git repository to a remote one. Use GitHub as the remote repository.

Create a new or use an existing GitHub account.

Create a repository.

On the New Repository page, enter the details of your repository.

  • Template: No template
  • Owner: your GitHub account
  • Respository name: same as your project folder
  • Description: okay to leave blank
  • Visibility: private

create-github-repo

Read the instructions on the page that GitHub redirects you.

quickstart-github-repo

The GitHub Quickstart instructions have six git commands.

Skip the first three: git init, git add README.md and git commit -m "first commit". You alread did them.

Entering git init again might cause problems.

Only do the last three and remember to use your repo url instead of https://github.com/thacash/example.git`.

git branch -M main
git remote add origin https://github.com/thacash/example.git
git push -u origin main

The fourth git command sets the name of your primary working branch to main. Enter in your terminal the following:

git branch -M main

The fifth command has you connect your local git to GitHub by storing the GitHub address in your git files. Before doing that, confirm that your local git does not yet have a remote.

In your terminal, type git remote.

git remote -v

Add a remote.

When you add a remote, you are telling the local git repository the address of where to find your remote repository.

To add a remote repository to your local git, enter the command from the Quickstart page for your repo.

git remote add origin https://github.com/your-github-name/your-repo-name.git

Check again your local git remote reference using git remote -v. This time you should see the terminal print origin and the GitHub address.

git remote -v

Now that you connected the local and remote repositories, save the local git history to your GitHub repo.

Remember above when you committed changes to the local git? Now you are going to push those committed changes to your GitHub repository.

When first connecting local to remote, the command is git push -u origin master.

git push -u origin main

The output in the terminal should be something like the following.

Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 770 bytes | 770.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com-yourname:yourname/your-repo.git
 * [new branch]      master -> master
Branch 'main' set up to track remote branch 'main' from 'origin'.

The current version of your project folder from your computer should now be saved to GitHub.

From here on out, you only need to use the command git push instead of git push -u origin main to push your local to the remote.

Visit your GitHub repository in a browser.

Make sure the page is freshly reloaded. You should see your commit in the repository. Your project folder from your computer should now be on GitHub.

Now delete the index.js file from your project folder and use this project folder for the rest of this course.

Make sure you install the NextJS Quickstart so that the .git file lives at the root of your NextJS project.

Visit your profile or sign in to automatically to create a profile and enter your GitHub username.

https://github.com/your-username

Build NextJS Quickstart

NextJS is an open-source web framework built on NodeJS and React-based web applications. They have a great Quickstart guide you should complete.

The guide teaches you the NextJS basics. Plus, afterwards you have the client-side pages, server-side architecture, and boilerplate code that you need for server-side rendering and generating static websites.

Complete the NextJS quickstart exactly as is.

Push your code to GitHub as you go.

While you should err on the side of pushing to git more often than not, try to not push broken versions. Get to a good stopping point unless you are in a hurry to stop but want to save your code.

The home page should look something like this.

nextjs-quickstart

The posts/[id].js page should look something like this.

nextjs-static-server-rendering

You should for sure do the NextJS Quickstart. In case you need the completed code, here is a link to the final Next.js quickstart demo code.

After completing the Quickstart, make a few modifications to the code in components/layout.js.

First, delete the posts/[id].js file. You soon change the structure of your application and will rebuild the client-side routes in this posts folder in the next chapter.

The purpose of components/layout.js for your application is to wrap all your client-side pages and provide common information and services to all of them.

You generally will not use it to display content between the four corners of the browser.

With that in mind.

  • Delete the header tags and the code inside
  • Change the name and siteTitle to something related to your topic
  • Delete the container style class from the outer-most div in the component
  • Delete the Image, Link, styles and other unused imports
  • Replace the main tags with div tags and add a style class to the div
  • Delete the link back to the homepage
  • Delete home from the layout function definition's list of parameters
  • Delete the file named components/layout.module.css

Here is the header tag to delete.

Components in NextJS
NextJS layout.js
<header className={styles.header}>
   {home ? (
      <>
         <Image
            priority
            src="/images/street.png"
            className={utilStyles.borderCircle}
            height={144}
            width={144}
            alt={name}
         />
            <h1 className={utilStyles.heading2Xl}>{name}</h1>
         </>
        ) : (
         <>
          <Link href="/">
            <a>
               <Image
                   priority
                   src="/images/profile.jpg"
                   className={utilStyles.borderCircle}
                   height={108}
                   width={108}
                   alt={name}
               />
            </a>
            </Link>
            <h2 className={utilStyles.headingLg}>
              <Link href="/">
                <a className={utilStyles.colorInherit}>{name}</a>
              </Link>
            </h2>
          </>
        )}
</header>

Here is the style to delete in components/layout.js from the outer-most div.

className={styles.container}

Here is an example of the div that should replace the main tag.

StackOverflow Explanation of Children
React Docs Children
<div className={utilStyles.pageWrapper}>{children}</div>

Here is the link to home that you should delete.

NextJS Props
{!home && (
   <div className={styles.backToHome}>
      <Link href="/">
         <a>← Back to home</a>
      </Link>
   </div>
)}

After adjusting components/layout.js, do the following in pages/index.js.

  • Import the name and siteTitle from components/layout.js and display them on the page
  • Remove from the page the Articles title and also the allPostsData loop
  • In getStaticProps, delete const allPostsData = getSortedPostsData() and then allPostsData data from the return statement

Here is an example import statement for items from layout.js.

import Layout, { name, siteTitle } from "../components/layout";

Here is the code to delete from pages/index.js.

<div>
  <h2>Articles</h2>
  <ul className={utilStyles.list}>
    {allPostsData.map(({ id, date, title }) => (
      <li className={utilStyles.listItem} key={id}>
        <Link href={`/posts/${id}`}>
          <a>{title}</a>
        </Link>
        <br />
        <small className={utilStyles.lightText}>
          <Date dateString={date} />
        </small>
      </li>
    ))}
  </ul>
</div>

Customize the styles just a little bit in styles/global.css and styles/utils.module.css

The goal is just to start working on style consistent with your topic. Pick colors and other details consistent with your topic and start working on general layout styling.

The goal is not production-ready styles. If you run into a style bug at this stage, we recommend surrendering to another decision instead of spending energy solving it now.
Use the pageWrapper styles on div in layout.js for layout styling. Think about using display: flex and the collection of flex-box parent and child properties, margin, padding, and maybe even the position properties with top, right, bottom, left. Try to keep it simple.

Take a screeenshot and share it to our Discord server and introduce yourself.

Create Categories

Think of three categories within your topic. These categories are how you will organize your content and your application.

In the NextJS Quickstart, you have a posts folder in the root of your project with two .md files. You are going to change the structure of that folder and use the category names as names of subfolders.

To help explain, here is an example.

The topic for the demo app for this course is mental health through actionable daily living activities.

The categories are motivation, energy, focus, and relaxation.

Come up with three category names.

Share your categories with us on our Discord server and get some early feedback.

In the posts folder in the root of your project, you have two .md files from the NextJS Quickstart.

In that folder, add four subfolders. Three of the subfolders should have the name of a category as their filename. The fourth should be named drafts.

For the existing .md files, change the file names and titles to one-example-post and the-other-example-post.

Add the following text to one-example-post.

Markdown Basic Syntax
Markdown Cheat Sheet
## Lorem ipsum dolor

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Fringilla ut morbi tincidunt augue interdum velit euismod in pellentesque. Tortor at auctor urna nunc id cursus metus aliquam. Purus in massa tempor nec feugiat nisl pretium fusce id. Sed odio morbi quis commodo odio aenean sed. Felis bibendum ut tristique et. Viverra vitae congue eu consequat ac felis. Orci a scelerisque purus semper eget. Ipsum dolor sit amet consectetur adipiscing elit ut aliquam. Lectus nulla at volutpat diam ut. Id aliquet risus feugiat in. Pharetra et ultrices neque ornare aenean euismod elementum. Maecenas sed enim ut sem viverra aliquet eget sit. Consectetur a erat nam at lectus urna duis convallis. Ornare arcu odio ut sem nulla pharetra diam. Ut diam quam nulla porttitor massa id neque aliquam. Quam nulla porttitor massa id neque aliquam.

## Senectus et netus et malesuada

Senectus et netus et malesuada fames ac turpis. Purus non enim praesent elementum facilisis. Cursus sit amet dictum sit amet justo donec. Mauris nunc congue nisi vitae suscipit tellus. Et netus et malesuada fames ac. Consectetur purus ut faucibus pulvinar elementum integer enim neque volutpat. Dolor sit amet consectetur adipiscing elit duis tristique. Vestibulum lorem sed risus ultricies tristique nulla aliquet. Vitae tempus quam pellentesque nec. Est ante in nibh mauris cursus mattis molestie a. Faucibus purus in massa tempor nec feugiat nisl pretium. Sed libero enim sed faucibus turpis in eu mi. Diam sit amet nisl suscipit adipiscing. Sed felis eget velit aliquet. Neque laoreet suspendisse interdum consectetur.

## Enim neque volutpat

Enim neque volutpat ac tincidunt vitae semper quis lectus nulla. Amet purus gravida quis blandit turpis cursus in. Non arcu risus quis varius quam quisque id diam. Mi proin sed libero enim sed faucibus turpis in eu. Aliquam ut porttitor leo a diam. Blandit massa enim nec dui nunc mattis enim ut. Commodo sed egestas egestas fringilla. Augue interdum velit euismod in pellentesque massa placerat duis ultricies. Lectus urna duis convallis convallis tellus. Penatibus et magnis dis parturient montes nascetur ridiculus mus mauris. Quis lectus nulla at volutpat diam ut venenatis tellus in. Quis enim lobortis scelerisque fermentum dui faucibus in ornare. Tristique et egestas quis ipsum suspendisse ultrices.

## Odio eu feugiat

Odio eu feugiat pretium nibh ipsum consequat nisl vel pretium. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque convallis. Habitant morbi tristique senectus et. Orci sagittis eu volutpat odio facilisis mauris sit. Felis eget nunc lobortis mattis aliquam faucibus purus in massa. Volutpat consequat mauris nunc congue nisi vitae suscipit tellus. Non consectetur a erat nam at lectus urna duis. Nunc faucibus a pellentesque sit. At erat pellentesque adipiscing commodo elit at. Sagittis eu volutpat odio facilisis mauris sit amet. At ultrices mi tempus imperdiet nulla malesuada pellentesque elit eget. Bibendum enim facilisis gravida neque convallis a cras semper auctor. Integer vitae justo eget magna. Nisi lacus sed viverra tellus in hac. Sed sed risus pretium quam vulputate. Cursus mattis molestie a iaculis at.

## Ut porttitor leo

Ut porttitor leo a diam sollicitudin tempor id eu nisl. Tempus imperdiet nulla malesuada pellentesque elit eget gravida cum. Consectetur adipiscing elit duis tristique sollicitudin nibh sit. Eget est lorem ipsum dolor. Porttitor leo a diam sollicitudin tempor id eu nisl. Et netus et malesuada fames ac. Aliquet lectus proin nibh nisl. Pharetra et ultrices neque ornare aenean euismod. Tristique senectus et netus et. Et leo duis ut diam quam nulla porttitor massa id. Adipiscing elit pellentesque habitant morbi tristique senectus et.

Add the following text to the-other-example-post.

Markdown Basic Syntax
Markdown Cheat Sheet
## Massa id neque aliquam

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Fringilla ut morbi tincidunt augue interdum velit euismod in pellentesque. Tortor at auctor urna nunc id cursus metus aliquam. Purus in massa tempor nec feugiat nisl pretium fusce id. Sed odio morbi quis commodo odio aenean sed. Felis bibendum ut tristique et. Viverra vitae congue eu consequat ac felis. Orci a scelerisque purus semper eget. Ipsum dolor sit amet consectetur adipiscing elit ut aliquam. Lectus nulla at volutpat diam ut. Id aliquet risus feugiat in. Pharetra et ultrices neque ornare aenean euismod elementum. Maecenas sed enim ut sem viverra aliquet eget sit. Consectetur a erat nam at lectus urna duis convallis. Ornare arcu odio ut sem nulla pharetra diam. Ut diam quam nulla porttitor massa id neque aliquam. Quam nulla porttitor massa id neque aliquam.

## Suspendisse interdum consectetur

Senectus et netus et malesuada fames ac turpis. Purus non enim praesent elementum facilisis. Cursus sit amet dictum sit amet justo donec. Mauris nunc congue nisi vitae suscipit tellus. Et netus et malesuada fames ac. Consectetur purus ut faucibus pulvinar elementum integer enim neque volutpat. Dolor sit amet consectetur adipiscing elit duis tristique. Vestibulum lorem sed risus ultricies tristique nulla aliquet. Vitae tempus quam pellentesque nec. Est ante in nibh mauris cursus mattis molestie a. Faucibus purus in massa tempor nec feugiat nisl pretium. Sed libero enim sed faucibus turpis in eu mi. Diam sit amet nisl suscipit adipiscing. Sed felis eget velit aliquet. Neque laoreet suspendisse interdum consectetur.

## Ipsum suspendisse ultrices

Enim neque volutpat ac tincidunt vitae semper quis lectus nulla. Amet purus gravida quis blandit turpis cursus in. Non arcu risus quis varius quam quisque id diam. Mi proin sed libero enim sed faucibus turpis in eu. Aliquam ut porttitor leo a diam. Blandit massa enim nec dui nunc mattis enim ut. Commodo sed egestas egestas fringilla. Augue interdum velit euismod in pellentesque massa placerat duis ultricies. Lectus urna duis convallis convallis tellus. Penatibus et magnis dis parturient montes nascetur ridiculus mus mauris. Quis lectus nulla at volutpat diam ut venenatis tellus in. Quis enim lobortis scelerisque fermentum dui faucibus in ornare. Tristique et egestas quis ipsum suspendisse ultrices.

## Molestie a iaculis at

Odio eu feugiat pretium nibh ipsum consequat nisl vel pretium. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque convallis. Habitant morbi tristique senectus et. Orci sagittis eu volutpat odio facilisis mauris sit. Felis eget nunc lobortis mattis aliquam faucibus purus in massa. Volutpat consequat mauris nunc congue nisi vitae suscipit tellus. Non consectetur a erat nam at lectus urna duis. Nunc faucibus a pellentesque sit. At erat pellentesque adipiscing commodo elit at. Sagittis eu volutpat odio facilisis mauris sit amet. At ultrices mi tempus imperdiet nulla malesuada pellentesque elit eget. Bibendum enim facilisis gravida neque convallis a cras semper auctor. Integer vitae justo eget magna. Nisi lacus sed viverra tellus in hac. Sed sed risus pretium quam vulputate. Cursus mattis molestie a iaculis at.

## Tristique senectus et

Ut porttitor leo a diam sollicitudin tempor id eu nisl. Tempus imperdiet nulla malesuada pellentesque elit eget gravida cum. Consectetur adipiscing elit duis tristique sollicitudin nibh sit. Eget est lorem ipsum dolor. Porttitor leo a diam sollicitudin tempor id eu nisl. Et netus et malesuada fames ac. Aliquet lectus proin nibh nisl. Pharetra et ultrices neque ornare aenean euismod. Tristique senectus et netus et. Et leo duis ut diam quam nulla porttitor massa id. Adipiscing elit pellentesque habitant morbi tristique senectus et.

Add image, role, and uid keys to each .md file.

Choose images that are hosted online or in your project folder. Set the role equal to a string containing comma-separated role names.

---
title: "One Example Post"
image: "https://images.pexels.com/photos/975771/pexels-photo-975771.jpeg?auto=compress&cs=tinysrgb&w=800"
role: "admin,subscriber,guest"
date: "2020-01-01"
uid: "1"
---
Add the uid to serve as a unique identifier so that you can change the filename later without messing up other parts of your application that depend upon the post having always the same id value.
You can use id as the name here if you want because you deleted the dynamic route posts/[id].js. So no naming conflict exists if you want to go with id.

Put copies of each .md file in each of your posts subfolders.

vs-code-folders

In lib/posts.js, write a function definition that meets the following criteria.

  • Is named getCategories
  • Gets the category names from the posts folder
  • Removes the drafts string from the resulting array
  • Returns the filtered array of strings
Use the existing function definitions in lib/posts.js as help.

Here is an example function definition for getCategories with console logs so that you can see the data as it moves through the code.

Geeks readdirsync
Mozilla let keyword
W3 Schools .filter() Method
let fileNames = fs.readdirSync(postsDirectory);
console.log(fileNames);
fileNames = fileNames.filter((item) => item !== "drafts");
console.log(fileNames);
return fileNames;
}

In pages/index.js, import getCategories.

Here is an example import statement.

import { getCategories } from "../lib/posts";

In pages/index.js, you should already have a function named getStaticProps. Inside getStaticProps, do the following.

  • Call getCategories
  • Save it to a variable named categories
  • Console log categories
NextJS Implement getStaticProps
NextJS Data Fetching in getStaticProps
JS Export Keyword
export async function getStaticProps() {
  const categories = getCategories();
  console.log(categories)
  return {
    props: {},
  };
}

Refresh the homepage in the browser.

You should see two console logs. Both in the terminal. Each console log has an array of strings.

One has the category names plus drafts. That is the pre-filtered array from the getCategories definition.

The other console log has your category names but not drafts. Those are the post-filtered copies.

The console logs from getCategories print to the terminal instead of the browser because it is not client-side code and getCategories is being called by getStaticProps, which is also not client-side code.

Delete the console logs after you test the code.
Your variable names should help your project and code tell a story to the human audience that reads your code. The computer does not care so much what the actual names are. But using clear, consise, and meaningful variable names can help the future you and other humans understand and maintain your code.

Next, in getStaticProps, do the following.

  • Return categories as static props and also console log categories
  • In the function definition for Home, add categories to the props
  • Add a useEffect that console logs the categories

Here is an example getStaticProps.

export async function getStaticProps() {
  const categories = getCategories();
  console.log(categories)
  return {
    props: {
      categories,
    },
  };
}

Here is an example useEffect.

React useEffect
React Hooks
useEffect(()=>{
  console.log(categories)
},[])

Refresh the homepage and check the browser and terminal console logs.

You should see two console logs. One in the terminal and the other in the browser. Each console log has an array of category names but not drafts.

The console log in getStaticProps prints to the terminal instead of the browser despite getStaticProps being in a client-side file because getStaticProps operates more like your server-side code.

The console log in the useEffect prints to the broswer because it is client-side code.

Delete the console logs.

In lib/posts.js, you have a function definition named getSortedPostsData.

The function gets the .md files from the posts folder. It was written before you added the subfolders.

Update it by doing the following.

  • Add a parameter named category to the function definition
  • Using a new variable, update the path in both readdirsync functions to point to the category subfolders

Here is an example.

Stack Overflow String Concatenation
W3 Schools .map()
W3 Schools path.join()
Geeks Node readfilesync
NPM Gray Matter
export function getSortedPostsData(category) {
  const categoryDirectory = postsDirectory + "/" + category;
  const fileNames = fs.readdirSync(categoryDirectory);
  const allPostsData = fileNames.map((fileName) => {
    const id = fileName.replace(/\.md$/, "");
    const fullPath = path.join(categoryDirectory, fileName);
    const fileContents = fs.readFileSync(fullPath, "utf8");
    const matterResult = matter(fileContents);
    return {
      id,
      ...matterResult.data,
    };
  });
  return allPostsData.sort((a, b) => {
    if (a.date < b.date) {
      return 1;
    } else {
      return -1;
    }
  });
}
The function returns the top part of the .md appearing between the two ---. In the Quickstart, each .md file contains a title and date in that section.

In pages/index.js, import getSortedPostsData.

Here is an example.

import { getCategories, getSortedPostsData } from "../lib/posts";

Do the following in getStaticProps.

  • Create a variable named categoriesObj and set it equal to an empty object
  • Use forEach to loop through categories
  • In each loop iteration, call getSortedPostsData and pass the category name as the argument
  • Save the return value of getSortedPostsData to a variable named postsData
  • Add a key to categoriesObj with the category name andn its value the array of posts data from getSortedPostsData
  • Console log and return categoriesObj
  • Change the parameter in Home from categories to categoriesObj

Here is an example function definition for getStaticProps.

W3 Schools .forEach()
W3 Schools JS Objects
export async function getStaticProps() {
  const categories = getCategories();
  const categoriesObj = {};
  categories.forEach((fileName) => {
    const postsData = getSortedPostsData(fileName);
    categoriesObj[fileName] = postsData;
  });
  console.log(categoriesObj);
  return {
    props: {
      categoriesObj,
    },
  };
}

Refresh the page. You should see an object in each console log. Each key in the object is a category and each value is an array of posts data.

{
  energy: [
    {
      id: '2',
      title: 'SSG-SSR',
      image: 'https://cdn.pixabay.com/photo/2017/04/12/07/46/tea-lights-2223898__340.jpg',
      role: 'admin,subscriber,guest',
      date: '2022-08-14'
    },
    {
      id: '1',
      title: 'Pre-Rendering',
      image: 'https://cdn.pixabay.com/photo/2017/04/12/07/46/tea-lights-2223898__340.jpg',
      role: 'admin,subscriber,guest',
      date: '2022-08-14'
    }
  ],
  focus: [
    {
      id: '2',
      title: 'SSG-SSR',
      image: 'https://cdn.pixabay.com/photo/2017/04/12/07/46/tea-lights-2223898__340.jpg',
      role: 'admin,subscriber,guest',
      date: '2022-08-14'
    },
    {
      id: '1',
      title: 'Pre-Rendering',
      image: 'https://cdn.pixabay.com/photo/2017/04/12/07/46/tea-lights-2223898__340.jpg',
      role: 'admin,subscriber,guest',
      date: '2022-08-14'
    }
  ],
  motivation: [
    {
      id: '2',
      title: 'SSG-SSR',
      image: 'https://cdn.pixabay.com/photo/2017/04/12/07/46/tea-lights-2223898__340.jpg',
      role: 'admin,subscriber,guest',
      date: '2022-08-14'
    },
    {
      id: '1',
      title: 'Pre-Rendering',
      image: 'https://cdn.pixabay.com/photo/2017/04/12/07/46/tea-lights-2223898__340.jpg',
      role: 'admin,subscriber,guest',
      date: '2022-08-14'
    }
  ],
  relaxation: [
    {
      id: '2',
      title: 'SSG-SSR',
      image: 'https://cdn.pixabay.com/photo/2017/04/12/07/46/tea-lights-2223898__340.jpg',
      role: 'admin,subscriber,guest',
      date: '2022-08-14'
    },
    {
      id: '1',
      title: 'Pre-Rendering',
      image: 'https://cdn.pixabay.com/photo/2017/04/12/07/46/tea-lights-2223898__340.jpg',
      role: 'admin,subscriber,guest',
      date: '2022-08-14'
    }
  ]
}
Delete the console logs.

In components/posts, create a file named categories.js with the following criteria.

  • In the function definition, add categoriesObj to the props
  • In the JSX statement, loop through categories and console log each category name
  • For each category, loop through the array of posts and console log each post

Here is an example filepath.

components/posts.categories.js

Here is an example boilerplate component.

NextJS Pages
Components in NextJS
JS Export Keyword
export default function Categories() {
  return (
    <div>Categories</div>
  );
}

Here is an example of the component with the code.

NextJS props
W3 Schools .map()
Mozilla JS Object
Destructuring Objects
Object Literal Property Shorthand
export default function Categories({ categoriesObj }) {
  return (
    <div>
      {Object.keys(categoriesObj).map((category) => {
        console.log(category);
        categoriesObj[category].map((post) => {
          console.log(post);
        });
      })}
    </div>
  );
}
The parameters list - { categoriesObj } is an example of destructuring an object.
You may also see that same syntax - `{ categoriesObj } - as shorthand for { categoriesObj: categoriesObj }.

In pages/index.js, import the new categories component and render it on the page. Pass categoryObj as a prop.

Here is an example import statement.

import Categories from "../components/posts/categories";

Here is an example of rendering the Categories component.

 <Categories categoriesObj={categoriesObj} />

Refresh the page and look for the console logs in the browser.

blog-categories-post

Replace the console log of the category name with JSX that puts the name on the page.

Use h2 tags for displaying the category name.

Mozilla Object Keys
Mozilla Object Entries
Stack Overflow Working With Objects
NextJS Headings
NextJS Headings
Yoast SEO
export default function Categories({ categoriesObj }) {
  return (
    <div>
      {Object.keys(categoriesObj).map((category) => {
        return (
          <div>
            <h2>{category}</h2>
            {Object.keys(categoriesObj).map((category) => {
              return categoriesObj[category].map((post) => {
                return <div>{console.log(post)}</div>;
              });
            })}
          </div>
        );
      })}
    </div>
  );
}

Refresh the page and look for the category names on the actual page.

Look in the console to see each post object logged individually. Also notice the following warning.

Why you need a key with .map() in React
Warning: Each child in a list should have a unique "key" prop.

In each .map(), update the code as follows to overcome the warning by adding a key attribute to returned div and set its value to category or post title

export default function Categories({ categoriesObj }) {
  return (
    <div>
      {Object.keys(categoriesObj).map((category) => {
        return (
          <div key={category}>
            <div>{category}</div>
            {Object.keys(categoriesObj).map((category) => {
              return categoriesObj[category].map((post) => {
                return <div key={post.title}>{console.log(post)}</div>;
              });
            })}
          </div>
        );
      })}
    </div>
  );
}

In components/posts, add a new file named postsList.js.

components/posts/postsList.js

Do the following.

  • In postsList, add category and categoryPosts to its list of props in the function definition
  • In components/categories.js, import postsList
  • Render postsList below the category title
  • Pass category and categoriesObj[category] as the values for category and categoryPosts
  • Move the map of the posts from categories to this new postsList component
  • In postsList, import the Link tag
  • Show the post image and title inside a Link tag that take you to /posts/{category}/{post.id}
  • Add a new stylesheet in styles named posts.module.css
  • Add some styles for the categories and postsList components

Here is the Link import.

NextJS Link
JS Import Keyword
import Link from "next/link";

Here is the Link tag.

<Link href={`/posts/${post.id}`} key={post.title}>
  <a>
    <img src={post.image} />
    <h3>{post.title}</h3>
  </a>
</Link>

Here is an example of postsList.

import Link from "next/link";
import postsStyles from "../../styles/posts.module.css";

export default function PostsList({ category, categoryPosts }) {
  return (
    <div className={postsStyles.postsWrapper}>
      {categoryPosts.map((post) => {
        return (
          <Link href={`/posts/${category}/${post.id}`} key={post.title}>
            <a className={postsStyles.postLink}>
              <img src={post.image} className={postsStyles.mainImage} />
              <h3>{post.title}</h3>
            </a>
          </Link>
        );
      })}
    </div>
  );
}

Here is an example of the refactored categories page.

export default function Categories({ categoriesObj }) {
  return (
    <div className={postsStyles.categoriesWrapper}>
      {Object.keys(categoriesObj).map((category) => {
        return (
          <div key={category} className={postsStyles.categoryWrapper}>
            <h2 className={postsStyles.categoryTitle}>{category}</h2>
            <PostsList
              category={category}
              categoryPosts={categoriesObj[category]}
            />
          </div>
        );
      })}
    </div>
  );
}
For style inspiration, look at existing websites with designs you like.
Try the links. They should take you to a page that does not yet exist. We broke it! In the next chapter, you fix it.

Take a screeenshot and share it to our Discord server to show us your work.

Push To Production

Follow the quickstart instructions on how to push your NextJS app to vercel.

Check your production build to make sure your application deployed successfully.

Login to Vercel. Click on your project and then Deployments. Select a deployment to view important information.

  • The Overview tab tells you about deployment build errors
  • The Functions tab has real-time logs

Checking the most-recent deployment is really helpful when debugging production issues.

  • The Overview tab helps you debug failed builds by showing you error messages
  • The Functions displays console logs like the terminal in development

Vercel creates a permanent link and a link for each deployment.

Find the permanent link so that you can see the newest deployment each time you visit.

Visit your profile or sign in to automatically to create a profile and enter your Vercel link.

Create Navbar

In /components, create a subfolder named navigation.

Add to the subfolder a new filed named Navbar.js.

NextJS Pages
Components in NextJS
components/navigation/Navbar.js

Add a simple navbar that sticks to the top of the page and spans the entire width.

nextjs-navbar

Here is sample JSX.

W3 Schools Navbar
W3 Schools Top Nav
import Link from "next/link";
import navbarStyles from "../../styles/navbar.module.css";

const Navbar = () => {
  return (
    <div className={navbarStyles.navbar}>
      <div className={navbarStyles.navbarLeft}>
        <Link href="/">
          <a className={navbarStyles.navbarLinkLeft}>Home</a>
        </Link>
      </div>
      <div className={navbarStyles.navbarRight}>
        <Link href="/posts">
          <a className={navbarStyles.navbarLinkRight}>Posts</a>
        </Link>
      </div>
    </div>
  );
};

export default Navbar;

Add to styles a new filed named navbar.module.css.

Add your navbar styles to that file.

Here is sample CSS. Use it as is or as a starting point for something else.

CSS Position
Flexbox Parent
Flexbox Child
.navbar {
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background-color: aliceblue;
  padding: 10px;
  z-index: 100;
}

.navbarLeft {
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

.navbarRight {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

.navbarLinkLeft {
  color: #0070f3;
  cursor: pointer;
  margin-right: 15px;
}

.navbarLinkRight {
  color: #0070f3;
  cursor: pointer;
  margin-left: 15px;
}

.navbarLinkLeft:hover,
.navbarLinkRight:hover {
  color: violet;
  margin-left: 15px;
}

Add the Navbar to the /components/layout.js file.

Stack Overflow _app.js
NextJS Custom App
NextJS layout.js
import Head from "next/head";
import Navbar from "./navigation/Navbar";

export const name = "Jungle Memory";
export const siteTitle = "Doing STEM";

export default function Layout({ children }) {
  return (
    <div>
      <Head>
        <link rel="icon" href="/favicon.ico" />
        <meta
          name="description"
          content="Learn how to build a personal website using Next.js"
        />
        <meta
          property="og:image"
          content={`https://og-image.vercel.app/${encodeURI(
            siteTitle
          )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
        />
        <meta name="og:title" content={siteTitle} />
        <meta name="twitter:card" content="summary_large_image" />
      </Head>
      <Navbar />
      <div className={utilsStyles.pageWrapper}>{children}</div>
    </div>
  );
}

Make the navbar responsive based on the screen size. It should look good on both mobile and deskstop screens.

Use media queries to change style properties based on screen size.

Below is an example set of media queries you can use at the bottom of navbar.module.css.

The example takes a mobile-first approach. This means the large-screen styles are in the media queries, and the general and small-screen styles are in the main part of the document.

Flexbox Responsive
Media Queries
.navbar {
  position: relative;
  top: 0;
  right: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-direction: column;
  background-color: aliceblue;
  padding: 10px;
  z-index: 100;
}

.navbarLeft {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
}

.navbarRight {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
}

.navbarLinkLeft {
  color: #0070f3;
  cursor: pointer;
  margin-right: 0;
}

.navbarLinkRight {
  color: #0070f3;
  cursor: pointer;
  margin-left: 0;
}

.navbarLinkLeft:hover,
.navbarLinkRight:hover {
  color: violet;
}

@media only screen and (min-width: 700px) {
  .navbar,
  .navbarLeft,
  .navbarRight {
    flex-direction: row;
    align-items: center;
  }

  .navbarLeft {
    justify-content: flex-start;
  }

  .navbarRight {
    justify-content: flex-end;
  }

  .navbarLinkLeft {
    margin-right: 15px;
  }

  .navbarLinkRight {
    margin-left: 15px;
  }
}
Notice how some style properties changed from the previous CSS example. What changes do you notice? How do those impact how things look and behave on the screen?

The result is that the links are in a row in a large screen and stacked vertically and centered in the small screen.

nextjs-responsive-navbar

Take a screeenshot and share it to our Discord server.

Setup User Sign In

Use Google to authenticate users. This eliminates the need for you to store and manage passwords or build other authentication features.

Your application will use the Google authentication for two main purposes:

  • Proving user identity
  • Getting basic user profile information to use and display within your app

Start by adding a sign in button to Navbar.js.

The sign in button should have an onClick event handler.

Have it console log the event object.

Console log
React Events
NextJS React State
 <button
    className={navbarStyles.navbarLinkRight}
    onClick={(e) => {
        console.log(e);
    }}
>
    Sign In
</button>
Notice onClick accepts a function definition as an argument instead of a variable name. This helps pass data into a function definition or use the event object.

As a development step, console log the event to make sure the setup is okay so far.

Take a moment to explore what you see in the console log. What is that?

Review the Next Auth introduction.

Install next-auth.

Next Auth
npm i next-auth

Follow and build the Next Auth Getting Started Guide for an Existing Project.

Use the Next Auth Google Provider as the only sign in option.

Here is example code for pages/api/auth/[...nextauth].js.

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";

const providers = [
  GoogleProvider({
    clientId: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  }),
];

export const authOptions = {
  providers,
  secret: process.env.NEXTAUTH_SECRET,
};

export default async function auth(req, res) {
  return await NextAuth(req, res, {
    providers,
  });
}
Make sure your .env file is listed in your .gitignore file.
List your Google Auth credentials and a made-up secret in your .env file and also add them to your Vercel environment variables.
Any time you update your .env file, you need to stop the the development server. Then restart it with npm run dev.
Any time you update your Vercel environment variables, you need to push a new commit to GitHub to create a new Vercel production build.

Add the relevant frontend code to your Navbar.js component and refactor it to fit your code.

In Navbar, use session with && to conditionally render the sign in button instead of using if / else to dictate what the component returns.

Replace the console log in the onClick with the signIn() function call and add a sign out button that invokes the signOut() function.

Next Auth Sign In
Next Auth Sign Out
Next Auth useSession
{!session && (
  <div
    className={navbarStyles.navbarLinkRight}
    onClick={() => {
      signIn();
    }}
  >
    Sign In
  </div>
)}
{session && (
  <div
    className={navbarStyles.navbarLinkRight}
    onClick={() => {
      signOut();
     }}
   >
    Sign Out
  </div>
)}

In Navbar, add a useEffect with session in its dependency array. Console log session so that you can confirm the authentication is working and see the user data that you get from Google.

React useEffect
React Hooks
useEffect(() => {
  console.log(session);
}, [session]);

Check that it works.

  • When a user is signed out, you should see the sign in button and null or undefined in the console
  • Clicking the sign in button should sign in the user with Google
  • When a user is signed in, you should see the sign out button and in the console an object containing the user's Google information
  • Clicking the sign out button should sign out the user with Google, bringing you back to the first step

Share your link to the Discord server to get some early users.

Deploy Domain With Analytics

Publish your application to your own custom domain. Until now, you have been hosting your application on a Vercel domain with .vercel.app at the end. Instead, use your own domain.

Vercel has amazing instructions on how to setup a custom domain. Follow those to complete the setup. You can use an existing domain or buy a new one.

If you buy a new domain, first do some research to make sure the name is available. Search the following places to better understand whether your name is available and distinct enough based on what already exists.

  • Domain name providers like GoDaddy
  • Facebook, Twitter, Instagram, TikTok, LinkedIn, Google, and any platform that is important for your niche
  • National, State, and Local government and public business listings or Trademark databases

The strongest names from a trademark point of view are those that are distinctive. The most distinctive names are arbitrary or fanciful. A name is arbitrary when it is an existing word but applied in an unusual way, like Apple for a technology company. A fanciful word is a made up word, like Keurig. What's a Keurig?

However, descriptive terms can be good too. They can be good for SEO and marketing purposes. Whichever direction you go, just make sure the name is available enough to be distinct from your competition.

And send us a link to your domain so that we can check it out and tell our friends about it!

Next, add Google Analytics to track the visitors to your application. Sign up and in to Google Analytics.

Visit the Google Analytics home page and click on the admin panel.

google-analytics-home

Select or create an account. Then create a property within that account.

google-analytics-admin

Select web.

google-analytics-stream

Enter your custom domain and on the ensuing page copy your Measurement ID.

google-analytics-domain

In your .env file, save your measurement id as NEXT_PUBLIC_GOOGLE_ANALYTICS.

NEXT_PUBLIC_GOOGLE_ANALYTICS="your-google-analytics-measurement-id"

Go to Vercel and add the NEXT_PUBLIC_GOOGLE_ANALYTICS environment variable.

In _app.js, add the following code.

<Head>
  {/* <!-- Global site tag (gtag.js) - Google Analytics --> */}
  {/* Global Site Tag (gtag.js) - Google Analytics */}
  <script
    async
    src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS}`}
  />
  <script
    dangerouslySetInnerHTML={{
      __html: `
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', '${process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS}', {
        page_path: window.location.pathname,
      });
    `,
    }}
  />
  <link rel="icon" href="/favicon.ico" />
</Head>

This code is in Google Analytics. You need to dig a little for it.

It is located at Google Analytics > Admin > Data Streams > Your Data Stream > View tag instructions > Install Manually.

google-analytics-g-tag

Stop and restart your development server and visit the page in the browser. Then visit Google Analytics. Do you see that you have a visitor?

Push your code to Vercel. This is not only important for the new code to be live, but you need to push for the new environment variable to take effect.

Visit your custom domain and then Google Analytics. Do you see the visitor?

Download the Google Analytics app to your phone to view Google Analytics on the go.

Test the performance of your application and get actionable tips.

The demo app performed pretty good.

https://app.thacash.com/wp-content/uploads/2022/08/nextjs-performance.png

Here is some feedback the demo app received in addition to having extra JS code.

web-performance-nextjs

To overcome this html language issue, the NextJS way to do it is by updating the Document.

Add the following file.

NextJS _document
pages/_document.js

Add the following code.

import { Html, Head, Main, NextScript } from "next/document";

export default function Document() {
  return (
    <Html lang="en">
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

Push to Vercel and test the performance again to make sure the issue is resolved.

nextjs-performance

Visit your profile or sign in to automatically to create a profile and enter your custom domain.

Tweet it out and tag us at @thacash_club.

Last updated Sept 6, 2022 at 11:42