thank u next.js

Learn how to use Next.js to spin up a Single Page Application serving Mean Girl quotes in under 15 minutes!

January 25, 2019

This article was originally posted on https://www.javascriptjanuary.com/blog/thank-u-nextjs

jquery, backbone, angular

One taught me love, one taught me patience, one taught me pain. Ok, maybe Ariana wasn’t talking about JavaScript frameworks, but I’m sure she’d agree the pain is the same.

As a designer turned developer my love affair with server side coding is a little more ‘Bad Romance’ than ‘Side to Side’.

I stumbled across Next.js when I first began exploring porting my personal site from Jekyll to Gatsby. Next.js is a lightweight framework for static and server-rendered applications, but did I dare attempt another love affair with a serverside framework that would surely break my heart?

Channeling my inner Taylor Swift I took the plunge and less than 5 minutes after running npm install --save next I had an SPA up and running without shedding a tear.

This gave me an idea - it always pains me when I’m at a technical conference and people don’t get my Mean Girls quotes like my girl Ari does.

Ariana as Regina George

Next.js can help me with this - I can create a modern “burn book” to serve the best mean girls quotes and get running faster than a bend-and-snap!

mkdir burnbook
cd burnbook
npm init -y
npm install --save react react-dom next

That’s all I need to start an app with Next.js. Because I’m too lazy to remember custom commands, I add the following to my package.json file.

{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}

To create pages for my app, I simply create a pages folder and add the pages I want.

mkdir pages
touch pages/index.js
touch pages/about.js
touch pages/quotes.js

In my index file, I create a basic React component.

const imageStyle = {
  flex: 1
}

const Index = () => (
  <div>
    <h1>Burn Book</h1>
    <p>How well do you know your mean girl quotes?</p>
    <img style={imageStyle} src="./static/img/thankunext.png" />
  </div>
)

export default Index

I also need an about page for those unaware of the holiday that is October 3rd.

export default () => (
  <div>
    <h1>About</h1>
    <p>It's always October 3rd in our hearts.</p>
  </div>
)

I’ll get to the quotes page in a minute, but first I want to create a navigation for my pages. I’ll create a new folder to hold shared components like a header.

mkdir components
touch components/Header.js

In my header file, I can take advantage of Next’s Link. Link allows us to transition between routes, in the example below with a url, or we can pass it a URL object as well.

import Link from 'next/link'

const linkStyle = {
  marginRight: 15
}

const Header = () => (
    <div>
        <Link href="/">
          <a style={linkStyle}>Home</a>
        </Link>
        <Link href="/about">
          <a style={linkStyle}>About</a>
        </Link>
        <Link href="/quotes">
          <a style={linkStyle}>Quotes</a>
        </Link>
    </div>
)

export default Header

I can now import this header into my index and about pages:

import Header from '.components/Header'

const imageStyle = {
  flex: 1
}

const Index = () => (
  <div>
    <Header />
    <h1>Burn Book</h1>
    <p>How well do you know your mean girl quotes?</p>
    <img style={imageStyle} src="./static/img/thankunext.png" />
  </div>
)

export default Index

When I run npm run dev Next.js will compile my burnbook app and serve it at http://localhost:3000, and I can navigate between my pages. That’s all it takes?

Next.js knows what I want

Wait, Next.js will automagically assume I want the files in my page directory to be served as their own routes? I don’t have to set up and fight with a router? A framework that understands my needs! <3

It’s time to serve some fetch quotes now! I created a file of all the most important Mean Girls quotes one needs to know to survive modern society in a file data/quotes.js

mkdir data
touch data/quotes.js

//data/quotes.js

const quotes = [const quotes = [{
  "quote": "Get in loser. We're going shopping.",
  "author": "Regina George",
  "id": "1",
  "imageUrl": "/shopping.gif"
},
{
  "quote": "On Wednesdays we wear pink",
  "author": "Karen Smith",
  "id": "2",
  "imageUrl": "/pink.gif"
},
{
  "quote": "Four for you, Glenn Coco! You go, Glenn Coco!",
  "author": "Damian dressed as Santa Claus",
  "id": "3",
  "imageUrl": "/glencoco.gif"
},
{
  "quote": "That’s why her hair is so big. It’s full of secrets.",
  "author": "Damian",
  "id": "4",
  "imageUrl": "/secrets.gif"
},
{
  "quote": "The Limit Does Not Exist",
  "author": "Cady Heron",
  "id": "5",
  "imageUrl": "/limit.gif"
},
{
  "quote": "Boo, you whore",
  "author": "Regina George",
  "id": "6",
  "imageUrl": "/booyouwhore.gif"
}]

export default quotes;

I’m going to import these into the quotes page I built to display them and create links to pages to serve their amazing accompanying gifs. Our components have been pretty simple so far, but we want to get our quotes array and pass them to our component to render. Basic React components accept a simple “props” argument, so we can import our quotes and map through them. Next offers a getInitialProps function to get data for our components.

import Link from 'next/link'
import quotes from '../data/quotes'

const Quotes = (props) => (
  <div>
    <h1>Burn Book</h1>
    <p>How well do you know your mean girl quotes?</p>
    <ul>
      {props.quotes.map((quote) => (
        <li key={quote.id}>
          <Link href={`/quote?id=${quote.id}`}>
            <a>{quote.quote}</a>
          </Link>
        </li>
      ))}
    </ul>
  </div>
)

Quotes.getInitialProps = async function() {
  return { quotes: quotes }
}

export default Quotes

Next.js makes learning React less scary

I know a lot of JavaScript developers who are scared to admit they haven’t learned React yet. As a chick formerly in that camp, Next.js was surprisingly easy to start building a React app.

A quick thing to point out in the example above, when mapping over a list, we need to provide a unique key for each iteratee.

Now that we have our list, let’s build a component to show each unique quote.

touch pages/quote.js

Link has already done the work for us to let Next know that quote should be a page rendered, so we now need to get the id of the quote and display that quote’s information in our new component. I installed lodash to quickly find quotes by their ids.

npm install --save lodash
import Layout from '../components/Layout.js'
import quotes from '../data/quotes'

import * as  _ from 'lodash'

const Quote = (props) => (
  <div>
    <Layout>
      <h1>{props.quote.quote}</h1>
      <div>
        <img src={'./static/img' + props.quote.imageUrl} />
      </div>
      <p>- {props.quote.author}
    </Layout>
  </div>
);

Quote.getInitialProps = async function(router) {
  const quote = _.find(quotes, {id:router.query.id});
  return { quote: quote }
}

export default Quote

Oops! I didn’t close one of my tags. But instead of letting me agonize trying to find a small error, Next gently points out my mistake.

nextjs syntax error

Next.js handles my mistakes with grace

I’m not a perfect person, sometimes I forget to close a tag, or import a file correctly. No learning from drama, Next.js let’s me clearly know my mistakes, no guessing games.

Moving on, I can now click through to all the different Mean Girls quotes. I want to do a few things to tidy up, like create a common layout file for styling, and a header file for SEO purposes to help spread Mean Girl love.

For my layout file, I just want something simple to include styles, a header and maybe footer in. You know, just girly things.

touch components/Layout.js
import Header from './Header'

const layoutStyle = {
  margin: 20,
  padding: 20,
  border: '1px solid #DDD',
  display: 'flex',
  flexDirection: 'column'
}

const Layout = (props) => (
  <div style={layoutStyle}>
    <Header />
    {props.children}
  </div>
)

export default Layout

I can refactor my pages to use the new layout like this:

import Layout from '../components/Layout.js'

const imageStyle = {
  flex: 1
}

const Index = () => (
  <div>
    <Layout>
      <h1>Burn Book</h1>
      <p>How well do you know your mean girl quotes?</p>
      <img style={imageStyle} src="./static/img/thankunext.png" />
    </Layout>
  </div>
)

export default Index

I also want to get a little crazy and put a button on the index page that will send the user to a random quote when they push it. To do this I need to get a random id, and tell the router to navigate to that page.

import Layout from '../components/Layout.js'
import Router from 'next/router'

import quotes from '../data/quotes'

const imageStyle = {
  flex: 1
}
function getQuote() {
  let randomQuote = Math.floor((Math.random() * (quotes.length -1)) + 1);
  Router.push({
    pathname: '/quote',
    query: { id: randomQuote }
  });
};

const Index = () => (
  <div>
    <Layout>
    <h1>Burn Book</h1>
    <p>How well do you know your mean girl quotes?</p>
    <img style={imageStyle} src="./static/img/thankunext.png" />
    <button onClick={getQuote}>Get a Random Quote</button>
    </Layout>
  </div>
)

export default Index

That’s all it takes to get a Next.js project up and going and with incredibly few lines of code, but this doesn’t even scratch the surface of everything Next.js can do.

Next.js let’s me call the shots about when to prefetch data I want

// A basic link won't prefetch the route
<Link href="/blog" />
// Using the prefetch attribute will prefetch the route
<Link prefetch href="/blog" />

Next.js allows me to customize as much as needed

I can configure Webpack to let me use my css processor of choice, can switch to client side rendering, and quickly do custom routing for cleaner URLS.

Now I handle it, and that shit’s amazing. <3

Ariana flipping off on treadmill