Next.js

  • Framework for creating pre-rendered React websites

  • relatively unopinionated framework

  • Next.js offers both

    • SSR (Server Side Rendering) - app's ability to convert HTML files on server into fully rendered HTML pages for client.

    • SSG (Static Site Generation) - tool that generates full static HTML website based on raw data and set of templates.

  • Pre-rendering from SSR improves performance and better search engine optimization (SEO)

>>> Development and Production Environments

  • During development, you’re building and running the application on your local machine. Going to production is the process of making your app ready to be deployed.

  • Since each environment has different goals, there's a lot that needs to be done to move an application from development to production, including: compiling, bundling, minifying, and code spliting.

  • Compiling - process of taking code in one language (developer friendly ie. JSX) and outputting it in another language or another version of that language. Web app code needs to be compiled into a version browsers can understand.

  • Bundling - process of resolving the dependencies and merging (‘packaging’) the files ('modules') into optimized bundles for the browser. The goal is to reduce the # of requests for files when a user visits a web page.

  • Minifying - process of removing unnecessary code formatting (ie. comments, spaces, and indents) without changing the code’s functionality. The goal is to improve the app's performance by decreasing file sizes.

  • Code Splitting - process of splitting the app's bundle into smaller chunks required by each endpoint (different URL's). The goal is to improve the app's initial load time by only loading the code required to run that page.

    • The files inside the pages/ directory will be automatically code split by Next.js into its own JS bundle during the build step.

>>> Rendering

  • process of converting the code you write in React into the HTML rep. of your UI.

  • With Next.js, three types of rendering methods are available:

    1. Server-Side Rendering (pre-rendering)

      • the HTML of the page is generated on a server for each request

    2. Static Site Generation (pre-rendering)

      • the HTML is generated on the server, but unlike server-side rendering, there is no server at runtime. Instead, content is generated once, at build time, when the application is deployed, and the HTML is stored in a CDN and re-used for each request.

    3. Client-Side Rendering (not pre-rendering)

      • the browser receives an empty HTML shell from the server along with the JavaScript instructions to construct the UI


> Getter Started

npx create-next-app@latest <my_app>
# or
yarn create next-app <my_app>

# if parsing error, go to eslintrc.json, and replace with:
# "extends": ["next/babel","next/core-web-vitals"]

After the installation is complete:

  • To start a development server –– npm run dev / yarn dev

  • View application on –– http://localhost:3000

  • Edit pages/index.js and see the updated result in your browser


> Pages & Routes

In Next.js, a page is a React Component exported from a file in the /pages directory.

Creating New Pages

  • To create new pages, add file endpoints to the /pages directory s.t. :

    • pages/index.js is on endpoint /

    • pages/posts/first-post.js is on endpoint /posts/first-post.js

Note: The page react component itself can have any name, but you must export it as a default export:

export default function componentName() {
		return <h1>First Post</h1>
}

Linking Pages (Components)

  • <Link> allows you to do Client-Side Navigation which means that the page transition happens using JavaScript, which is faster than the default navigation done by the browser

  1. First, open pages/index.js, and import the Link component from next/link by adding this line at the top:

    import Link from 'next/link'
  2. Then wrap the <a> tag with a <Link> tag whose href-property is the relative path of the page (component) being linked to.

    // links the current <a> tag to pages/posts/first-post.js
    <Link href="/posts/first-post">
        <a>this page!</a>
    </Link>

Note:

  • If you need to link to an external page outside the Next.js app, just use an <a> tag without Link.

  • If you need to add attributes like, for example, className, add it to the atag, not to the Link tag


Adding other components:

  • If you want to make, say, a Navbar.js component for your app, make a new directory for those kind of components. Don't put them in any of the pre-existing directories.

  • ie. put Navbar.js in components/


Assets, Metadata, CSS

  • Next.js has built-in support for CSS & SASS

[ Assets ]

  • Next.js can serve static assets, ie. images, under the /public directory.

  • The /public directory is also useful for robots.txt, Google Site Verification, and any other static assets.

[ and Image Optimization ]

  • With the regular HTML tag, you would have to manually handle:

    • Ensuring your image is responsive on different screen sizes

    • Optimizing your images with a third-party tool or library

    • Only loading images when they enter the viewport

  • Next.js' component handles all the above for you.

    import Image from 'next/image'
    
    const YourComponent = () => (
      <Image
        src="/images/profile.jpg" // Route of the image file
    	  alt="Your Name"
    
        // Desired size with correct aspect ratio
        height={144}
        width={144}
      />
    )
  • <Head> is a React Component that is built into Next.js. It allows you to modify the <head> of a page.

  • You can import the Head component from the next/head module:

    import Head from 'next/head'
    
    export default function Home() {
      return (
      	<>
        <Head>
          <title>First Post</title>
          {/*other tags you would put in head, put here.*/}
        </Head>
        <div>
        	{/*etc...*/}
        </div>
      )
    }

[3rd-Party JavaScript ]

  • 3rd-party JS refers to any scripts that are added from a third-party source.

  • In addition to metadata, scripts that need to load and execute as soon as possible are usually added within the <head> of a page.

  • Instead of the HTML tag, use Next.js' tag:

    // In the same file, add an import for Script from next/script 
    import Script from 'next/script'
    
    // ... ie. (w/ additional properties strategy, onLoad)
    <Head>
    	<title>First Post</title>
    </Head>
    
    <Script
    	src="https://connect.facebook.net/en_US/sdk.js"
    	strategy="lazyOnload"
      onLoad={() =>
         console.log("script loaded correctly")
      }
    />

[ CSS Styling ]

  1. You can use a library called styled-jsx which is a "CSS-in-JS" library that lets you write CSS within a React component, and the CSS styles will be scoped (other components won’t be affected).

    <style jsx>{`
    	.container {
    			min-height: 100vh;
          padding: 0 0.5rem;
    	}
    
    	main {
    			padding: 5rem 0;
          align-items: center;
    	}
    `}</style>
  2. If not 1), then you can just use the regular import-css styling method. Next.js has built-in support for .css and .scss files. Ie:

    • To apply css styles for a Layout.js , you would create a layout.module.css and add the styles in there ie.

      /* layout.module.css */
      .container {
      		min-height: 100vh;
          padding: 0 0.5rem;
      }
    • Then, you would import the css file in the React Component file:

      // Layout.js
      import styles from './layout.module.css
    • The, to use the .container class in Layout.js, use styles.container as the className for the HTML to apply the styles:

      // Layout.js
      export default function Layout({ children }) {
        return
        	<div className={styles.container}>
            {children}
        </div>
      }

[ Global CSS Styling ]

  • Create a styles/global.css and add global styles in there.

  • To load this global.css styling to every page, create a file called pages/_app.js with the following content:

    import '../styles/global.css'
    
    export default function App({ Component, pageProps }) {
      return <Component {...pageProps} />
    }
  • The imported global.css will be applied to all pages since this App component is the top-level component which will be common across all the hanimpages.

  • Note: You need to restart the dev. server when you add pages/_app.js.


Pre-Rendering

  • By default, Next.js pre-renders every page, meaning it generates HTML for each page in advance, instead of having it all done by client-side JS

  • Results in better performance and SEO

  • Pre-rendering also allows your app to work w/o JS.

Hydration –– Each generated HTML is associated with minimal JavaScript code necessary for that page. When a page is loaded by the browser, its JavaScript code runs and makes the page fully interactive. This process is called hydration.

![Screen Shot 2022-04-12 at 6.34.58 PM](/Users/dev/Library/Application Support/typora-user-images/Screen Shot 2022-04-12 at 6.34.58 PM.png)

![Screen Shot 2022-04-12 at 6.35.15 PM](/Users/dev/Library/Application Support/typora-user-images/Screen Shot 2022-04-12 at 6.35.15 PM.png)

There are 2 Forms of Pre-Rendering, and the core difference b/n the two being when it generates the HTML for the page:

1. Static Site Generation - (SSG)

  • pre-rendering method that generates the HTML at build time. The pre-rendered HTML is then reused on each request

![Screen Shot 2022-04-12 at 6.38.09 PM](/Users/dev/Library/Application Support/typora-user-images/Screen Shot 2022-04-12 at 6.38.09 PM.png)

2. Server-Side Rendering - (SSR)

  • pre-rendering method that generates the HTML on each request.

![Screen Shot 2022-04-12 at 6.38.21 PM](/Users/dev/Library/Application Support/typora-user-images/Screen Shot 2022-04-12 at 6.38.21 PM.png)

(Note: In dev. mode, pre-rendering occurs when you run yarn dev)

Next.js lets you choose which pre-rendering form to use for each page!!

You should ask yourself: "Can I pre-render this page ahead of a user's request?" If the answer is yes, then you should choose SSG.

SSG with & w/o Data

Pages which don't require fetching external data will automatically be statically generated when the app is built for production.

![Screen Shot 2022-04-12 at 6.45.42 PM](/Users/dev/Library/Application Support/typora-user-images/Screen Shot 2022-04-12 at 6.45.42 PM.png)

However, for some pages, you might not be able to render the HTML without first fetching some external data (file system, API, DB, ...)

![Screen Shot 2022-04-12 at 6.46.51 PM](/Users/dev/Library/Application Support/typora-user-images/Screen Shot 2022-04-12 at 6.46.51 PM.png)

SSG with Data via getStaticProps

  • In Next.js, when you export a page component, you can also export an async function called getStaticProps.

  • This way, inside the function, you can fetch external data and send it as props to the page.

export async function getStaticProps() {
  // Get external data from, say, an API
  const res = await fetch('endpoint');
  const data = await res.json(); // JSON data in 'data'  

  // Value of 'props' key will be passed to Home comp.
  return {
    props: { users: data }
  }
}

// now you pick up the props from above function
export default function Home( {users} ) {
  return (
  	<div>
      <h1>All users</h1>
			{/*..handle data here..*/}
    </div>
  )
}
  • Essentially, getStaticProps allows you to tell Next.js:

    " Hey, this page has some data dependencies — so when you pre-render this page at build time, make sure to resolve them first! "


Gray-Matter (file system data parser)

  • gray-matter is a npm package which lets us parse metadeta in .md files formatted a certain way:

    ---
    title: 'Title'
    date: '04-12-2022'
    ---
    
    ContentContentContent
yarn install gray-matter

An example implementation.


SSR with Data

  • To use SSR, you need to export getServerSideProps instead of getStaticProps from your page:

    export async function getServerSideProps(context) {
      return {
        props: {
          // props for your component
        }
      }
    }
  • Since SSR is called at request time, its parameter context contains request specific params.

  • You should use getServerSideProps only if you need to pre-render a page whose data must be fetched at request time.

SWR

  • The team behind Next.js has created a React hook for data fetching called SWR.

  • It's highly recommended for when you're fetching data on the client side. It handles caching, revalidation, focus tracking, refetching on interval, and more.

  • see more here


Custom 404 Page

  • To make a custom 404 page, create a 404.js file in the /pages directory. This is a specially-named file which handles all invalid endpoints.

import Link from 'next/link'

export default function NotFound() {
  return (
  	<div>
    	<h1>Oops...</h1>
      <h2>That page cannot be found.</h2>
      <p>Go back to the <Link href="/"><a>Homepage</a></Link></p>
    </div>
  )
}

Redirecting Users

  • To automatically redirect users to a link, use the useEffect and useRouter hooks.

    • useEffect fires the useRouter once the user mounts

    • useRouter does the actual redirecting

    ie. A 404 Page which redirects user to homepage automatically after 3s

    // 404.js
    
    import Link from 'next/link'
    import { useEffect } from 'react'
    import { useRouter } from 'next/router'
    
    export default function NotFound() {
      const router = useRouter();
      
      useEffect(() => {
        setTimeout(() => {
        		// router.go(-1) // Go -1 back in history
          router.push('/');	// redirect to homepage
        }, 3000)
      }, [])
      
      return (
      	<div>
        	<h1>Oops...</h1>
          <h2>That page cannot be found.</h2>
          <p>Go back to <Link href="/"><a>Homepage</a></Link></p>
        </div>
      )
    }

Dynamic Routes

  • Instead of making separate pages for: /posts/1 and /posts/2, etc... , you can statically generate pages w/ dynamic routes: /posts/[id]

We can do this by creating a page file: pages/posts/[id].js

––> Pages that begin with [ and end with ] are dynamic routes in Next.js!

![Screen Shot 2022-04-13 at 3.27.51 PM](/Users/dev/Library/Application Support/typora-user-images/Screen Shot 2022-04-13 at 3.27.51 PM.png)

See implementation here.

![Screen Shot 2022-04-13 at 3.49.05 PM](/Users/dev/Library/Application Support/typora-user-images/Screen Shot 2022-04-13 at 3.49.05 PM.png)


Last updated