Next.js (Part 2)
Neil Haddley • November 6, 2021
getStaticProps, getServerSideProps, getStaticPaths and useSWR
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
getServerSideProps
I used getServerSideProps at runtime to build a page in response to each request.

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

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

The out folder contained the generated pages

/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

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