Server-Side Rendering with Next.js and TypeScript

In the official tutorial for creating a Next.js app, the web app that we will learn is to build a static website with JavaScript and Markdown. This article will teach us to build the server-side version of the app by consuming a simple JSON REST API from my previous article, Simple REST API with TypeScript.

Static Generation vs Server-Side Rendering

Before we jump into the code, let me explain what Static Generation and Server-Side Rendering are.

Static Generation

Static Generation means generating static HTML files at build time. This is the best approach for building a fast website. Static HTML files are faster than Server-Side Rendering and good for search engines. It is good for pages that don't frequently update and are publicly available.

Server-Side Rendering

Server-Side Rendering means generating the HTML on each request. The server generates the output for each incoming request. The server will fetch the data from the external API or the database. This process is the reason why it is slower than Static Generation. We also need to scale the server if requests grow. SSR is good for pages that frequently update or for websites that are handled by CMS (Content Management System).

Why Server-Side Rendering?

We know that static HTML files are faster than SSR. But letting users face the code and write pages in Markdown is a bad experience even if MD files are easy to learn. So, the best approach is the give users CMS. CMS is private and requires authentication and authorization. So, to bring a good user experience on CMS, the best approach is using Client-Side Rendering, because JavaScript can update the page without reloading the entire page. Public pages need to be friendly to search engines. The CSR can't make it. That's why we chose SSR.

Tutorial

The completed code can be found at https://github.com/aristorinjuang/next-server-side. Let me explain only the important parts here.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
export const getServerSideProps: GetServerSideProps = async () => {
  const response = await axios.get(`${process.env.HOST}/v1/articles`)
  const articles = Object.entries(response.data)

  return {
    props: {
      articles
    }
  }
};

You can find the code above in pages/index.tsx. Use getServerSideProps to send properties to the Home function for each request. I fetched the data from the external API using Axios in this example. You can change it to fetch data from the database too.

1
2
3
4
5
6
7
8
9
export type Article = {
  title: string
  description: string
}
type Articles = Map<number, Article>

type props = {
  articles: Articles
}

The result JSON from the Simple REST API here is a little unusual. It is a Map instead of an Article[]. You can change it to your own.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
type props = {
  article: Article
}

export default function Post(props: props) {
  if (props.article === undefined) {
    return (
      <Custom404 />
    )
  }
  return (
    <Layout>
      <Head>
        <title>{props.article.title}</title>
      </Head>
      <article>
        <h1 className={utilStyles.headingXl}>{props.article.title}</h1>
        <div dangerouslySetInnerHTML={{ __html: props.article.description }} />
      </article>
    </Layout>
  )
}

export const getServerSideProps: GetServerSideProps = async ({ params }) => {
  try {
    const response = await axios.get(`${process.env.HOST}/v1/articles/${params?.id}`)
    const article = Object.entries(response.data)[0][1]

    return {
      props: {
        article
      }
    }
  } catch (error) {
    console.error(error)

    return {
      props: {}
    }
  }
}

I moved [id].tsx to pages/[id].tsx. It made the routing to be /<id>. Still use getServerSideProps to fetch data for each request, but I use try and catch here. I printed the error in the stdout or terminal and returned undefined. So, if the article is undefined, I can return the custom 404 page.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
export default function Custom404() {
  return <Layout>
    <Head>
      <title>Not Found</title>
    </Head>
    <article>
      <h1 className={utilStyles.headingXl}>Not Found</h1>
      <p>The article that you are looking for is not found.</p>
    </article>
  </Layout>
}

I created a custom 404 page in pages/404.tsxinstead of using the built-in 404 page, import Error from 'next/error'. So, my 404 page has the same layout as the blog post.

Related Articles

Comments