Next.js (Part 2)

Neil HaddleyNovember 6, 2021

getStaticProps, getServerSideProps, getStaticPaths and useSWR

Pre-rendering

Next.js has two forms of pre-rendering: Static Generation and Server-side Rendering. The difference is in when Next.js generates the HTML for a page.

Static Generation is a pre-rendering approach that generates the HTML pages at build time.

Server-side Rendering is a pre-rendering approach that generates the HTML on each request.

You can create a Next.js app that uses Static Generation for some pages and Server-side Rendering for others.

Static Generation (with data)

In Next.js, after exporting a page function, a developer can export a getStaticProps function.

A getStaticProps runs at build time.

getStaticProps

A getStaticProps function can be used to generate a page based on data returned by a web service call*.

* You should not fetch an API Route from getStaticProps or getStaticPaths. Instead, write your server-side code directly in getStaticProps or getStaticPaths (or call a helper function).

getStaticProps

getStaticProps

getServerSideProps

A getServerSideProps function is used at runtime to build a page in response to a request (with or without caching).

getServerSideProps

getServerSideProps

getStaticPaths

getStaticProps and getStaticPaths can be used together to generate multiple pages.

getStaticProps and getStaticPaths being used together

getStaticProps and getStaticPaths being used together

next export

next export can be used to generate html pages that can be uploaded to a static web server.

$ npm run export

npm run export

npm run export

out folder with generated pages

out folder with generated pages

/out/articles/36

/out/articles/36

useSWR

Once the server-side rendered parts of a page have been downloaded JavaScript running on the page can fetch data and populate the remaining parts of the page (client-side).

$ npm install swr

Catch all routes

Dynamic routes can be extended to catch all paths by adding three dots (...) inside the brackets. For example:

pages/post/[...slug].js matches /post/a, but also /post/a/b, /post/a/b/c and so on.

pages/fullname/[...slug]/index.js matches /fullname/neil, but also /fullname/neil/haddley, /fullname/neil/leonard/haddley and so on.

You can use names other than slug, such as: [...param]

Client-side code

Client-side code

Catch all routes

Catch all routes

pagesarticlesindex.js

TEXT
1function index({ articles }) {
2    return (
3        <div>
4            <ul>
5                {articles.map(article => (<li key={article.id}>{article.title}</li>))}
6            </ul>
7        </div>
8    )
9}
10
11export default index
12
13export const getStaticProps = async () => {
14    const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=6')
15    const articles = await res.json();
16
17    return {
18        props: {
19            articles: articles
20        }
21    }
22}

pagesarticlesidindex.js

TEXT
1function article({ article }) {
2
3    return (
4        <div>
5            <h1>{article.title}</h1>
6            This is article {article.id}
7            <p>{article.body}</p>
8        </div>
9    )
10}
11
12export default article
13
14export const getServerSideProps = async (context) => {
15    const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${context.params.id}`)
16    const article = await res.json()
17    return {
18        props: {
19            article: article
20        }
21    }
22}

pagesarticlesidindex.js

TEXT
1function article({ article }) {
2
3    return (
4        <div>
5            <h1>{article.title}</h1>
6            This is article {article.id}
7            <p>{article.body}</p>
8        </div>
9    )
10}
11
12export default article
13
14export const getStaticProps = async (context) => {
15    const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${context.params.id}`)
16    const article = await res.json()
17
18    return {
19        props: {
20            article: article
21        }
22    }
23}
24
25export const getStaticPaths = async () => {
26    const res = await fetch(`https://jsonplaceholder.typicode.com/posts`)
27    const articles = await res.json()
28    const ids = articles.map(article => (article.id))
29    const paths = ids.map(id => ({ params: { id: id.toString() } }))
30
31    return {
32        paths,
33        fallback: false
34    }
35}

pagesarticlesidindex.js

YAML
1import {useRouter} from 'next/router'
2import useSWR from 'swr'
3
4function article() {
5
6    const router = useRouter()
7    const {id} = router.query
8
9    if (!id) return <div>waiting...</div>
10
11    const fetcher = (...args) => fetch(...args).then(res => res.json())
12
13    const { data, error } = useSWR(`https://jsonplaceholder.typicode.com/posts/${id}`, fetcher)
14
15    if (error) return <div>failed to load</div>
16    if (!data) return <div>loading...</div>
17    
18    return (
19        <div>
20            <h1>{data.title}</h1>
21            This is article {data.id}
22            <p>{data.body}</p>
23        </div>
24    )
25}
26
27export default article

pagesfullname...slugindex.js

TEXT
1import {useRouter} from 'next/router'
2
3function name() {
4
5    const router = useRouter()
6    const {slug} = router.query
7
8    if (!slug) return <div>waiting...</div>
9
10    return (
11        <div>
12            <h1>{slug.join(' ')}</h1>
13        </div>
14    )
15}
16
17export default name