Next.js (Part 2)

Neil HaddleyNovember 6, 2021

getStaticProps, getServerSideProps, getStaticPaths and useSWR

Reactnext-jsget-static-propsswrreact

Pre-rendering

I explored Next.js's two forms of pre-rendering: Static Generation and Server-side Rendering. Static Generation generates HTML at build time; Server-side Rendering generates it on each request. I used both in the same app — different pages used different strategies.

Static Generation (with data)

After exporting a page function, I also exported a getStaticProps function. It runs at build time to fetch data and pass it as props to the page.

getStaticProps

I used getStaticProps 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

I used getServerSideProps at runtime to build a page in response to each request.

getServerSideProps

getServerSideProps

getStaticPaths

I used getStaticProps and getStaticPaths together to generate multiple pages at build time.

getStaticProps and getStaticPaths being used together

getStaticProps and getStaticPaths being used together

next export

I used next export to generate static HTML pages that I could upload to a static web server. I ran npm run export to produce the output.

npm run export

npm run export

The out folder contained the generated pages

The out folder contained the generated pages

/out/articles/36

/out/articles/36

useSWR

After the server-side rendered parts of a page downloaded, I used useSWR to fetch data client-side and populate the remaining parts of the page. I installed it with npm install swr.

Catch all routes

I used catch-all routes 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.

I used names other than slug, such as [...param].

Client-side code

Client-side code

Catch all routes

Catch all routes

pagesarticlesindex.js

JAVASCRIPT
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

JAVASCRIPT
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

JAVASCRIPT
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

JAVASCRIPT
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