Skip to main content

React Ecosystem: Static Generation with Next.js and React

In the previous post, We created a BlogPost application rendering content on server-side with Next.js. We will be changing the same application to use SSG (Static Generation) capability of Next.js.

What will we create and which Next.js functions will we use?

We will create an application which will have index page to list all the available blog links and statically generated blog posts. We will use getStaticProps and getStaticPaths functions from Next.js.

getStaticProps function is called by Next.js at build time and pre-renders the page.

getStaticPaths function is also called by Next.js at build time. If you are using getStaticProps and there are dynamic paths then you define those in getStaticPaths

Update index.tsx

import React from 'react';
import { GetStaticProps, GetStaticPropsContext } from 'next';
import BloggerService from '../service/BloggerService';
import styles from '../App.module.css'

/* Line 1*/ interface IndexPageProps {
  blogLinks: Array<{ id: string, title: string }>
}


export default function IndexPage(props: IndexPageProps) {
  /* Line 2 */ return (
    <div className={ styles['blog-container'] }>
      <ul className={ styles['blog-posts'] }>
      {props.blogLinks.map(blogLink => <li key={blogLink.id}><a href={`/posts/${encodeURIComponent(blogLink.id)}`}>{blogLink.title}</a></li>)}
      </ul>
    </div>
  );
}

/* Line 3 */ export const getStaticProps: GetStaticProps = async (context: GetStaticPropsContext<any>) => {
  const bloggerPosts = await BloggerService.getAllPosts();
  const blogLinks = bloggerPosts.posts.map(post => {
    const splittedId = post.id.split('-')
      return {
        id: splittedId[splittedId.length - 1],
        title: post.title
      }
  });

  return {
    props: {
      blogLinks
    }
  }
}

At line 1, we defines the props type expected by IndexPage function. At Line 2, we are returning React.Element. We are iterating on the blogLinks passed as props to this component and returns a list. This property is passed from getStaticProps function at build time. At Line 3, we are defining getStaticProps function. We are calling BloggerService and retrieving blog feeds from https://codefoundry.dev. Then, we are extracting two properties id and title and are returning those from this method.

At the build time, Next.js will call this function on server-side and will pass these properties to IndexPage function and will generate static HTML on server. That static HTML will be served everytime.

Creating [slug].tsx to catch all the routes

Next step is to create [slug].tsx under pages/posts folder. This component will catch all the dymaically generated URL and will open the statically generated HTML if found otherwise endup showing 404 page.

import React from 'react';
import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next';
import IBlogPost from '../../models/IBlogPost';
import BloggerService from '../../service/BloggerService';
import BlogPost from '../../components/BlogPost';
import styles from '../../App.module.css';

/* Line 1 */ interface IServerProps {
  post: IBlogPost
}

export default (props: IServerProps) => {
  /* Line 2 */ return (
    <>
      <div className={styles['App-Container']}>
        <BlogPost post={props.post} />
      </div>
    </>
  );
}

/* Line 3 */ export const getStaticPaths: GetStaticPaths = async() => {
  const bloggerPosts = await BloggerService.getAllPosts();
  const paths = bloggerPosts.posts.map(post => {
    const splittedId = post.id.split('-')
    const slug = splittedId[splittedId.length - 1]
    return {
      params: {
        slug
      }
    }
  });

  return {
    paths,
    fallback: false
  }
}

/* Line 4 */ export const getStaticProps: GetStaticProps = async(context: GetStaticPropsContext<any>) => {
  const slug = context?.params?.slug ?? 'na'
  const bloggerPosts = await BloggerService.getAllPosts();

  const post = bloggerPosts.posts.find( post => post.id.endsWith(slug))
  return {
    props: {
      post
    }
  }
} 

At Line 1, we are creating type for props this component takes. At Line 2, we are returning React.Element. We are passing post property from props to BlogPost component. This property is passed by getStaticProps function at build time.

At Line 3, we are defining getStaticPaths function. This function returns the list of dynamic paths this page will handle.

At Line 4, we are defining getStaticProps function. This function reads the slug property from context's params property. This proeprty is generated by getStaticPaths function(Line3) and then get the post from BloggerService and finally this post property is passed to this page (Line 2).

I have modified existing code for this example. Let's cleanup code which is not required.

First, remove unwanted libraries with npm remove @reduxjs/toolkit react-redux next-on-netlify command. Remove BlogSearch.tsx, BlogListing.tsx, BlogPosts.tsx and BlogPosts.module.css from src/components folder. Remove index.css and index.tsx from src folder. Update _app.tsx under src/pages and remove redux store and provider. That's it.

Let's run the application.

Click on any link and you will see statically generated blog page.

That's it :). You can download the full code from github.

Recap

In this post, we introduced getStaticPaths and getStaticProps methods of Next.js for Static site generation. We used both methods in [slug].tsx for dynamic path generation. At last, we removed unwanted files from last code example.

What's next?

In the next post, we will deploy Next.js project (Static Generation and SSR) on Netlify.

Comments

Popular posts from this blog

Data Analytics: Watching and Alerting on real-time changing data in Elasticsearch using Kibana and SentiNL

In the previous post , we have setup ELK stack and ran data analytics on application events and logs. In this post, we will discuss how you can watch real-time application events that are being persisted in the Elasticsearch index and raise alerts if condition for watcher is breached using SentiNL (Kibana plugin). Few examples of alerting for application events ( see previous posts ) are: Same user logged in from different IP addresses. Different users logged in from same IP address. PermissionFailures in last 15 minutes. Particular kind of exception in last 15 minutes/ hour/ day. Watching and alerting on Elasticsearch index in Kibana There are many plugins available for watching and alerting on Elasticsearch index in Kibana e.g. X-Pack , SentiNL . X-Pack is a paid extension provided by elastic.co which provides security, alerting, monitoring, reporting and graph capabilities. SentiNL is free extension provided by siren.io which provides alerting and reporting function

React Ecosystem: Building BlogPost application with React and React Hooks

Building a Blog Post application Let's create a blog post application. It will have below features: Option to search blog posts. Option to list blog posts. Option to show blog post. Create a new project with npx create-react-app react-blog-posts --template typescript . Create BlogPosts.tsx component under src/components folder and IBlogPost model under src/models . import React from 'react'; import IBlogPost from '../models/IBlogPost'; interface IBlogPostsProps { posts: Array<IBlogPost> } function BlogPosts(props: IBlogPostsProps) { return ( <div className="blog-container"> <ul className="blog-posts"> { props.posts.map(post => <li key={post.id}>{post.title}</li>) } </ul> </div> ); } export default BlogPosts; interface IBlogPost { id: number title: string content: string author: string postedOn: string tags: string[]

Java 8 - Lambda expressions

In this post, we will cover following topics. What are Lambda expressions? Syntax for Lambda expression. How to define no parameter Lambda expression? How to define single/ multi parameter Lambda expression? How to return value from Lambda expression? Accessing local variables in Lambda expression. Target typing in Lambda expression. What are Lambda expressions? Lambda expressions are the first step of Java towards functional programming. Lambda expressions enable us to treat functionality as method arguments, express instances of single-method classes more compactly. Syntax for Lambda expression Lambda has three parts: comma separated list of formal parameters enclosed in parenthesis. arrow token -> . and, body of expression (which may or may not return value). (param) -> { System.out.println(param); } Lambda expression can only be used where the type they are matched are functional interfaces . How to define no parameter Lambda expression? If the la