In a recent article I gave an introduction to React. That article shows the first steps of building React components, but leaves us very far from building a useful web application. In this article we will learn how to create a real world application using Next JS.

Next JS

Next JS sells itself as the “React framework for production”. They promise smooth user experience combined with a rich set of features to build fast apps.

Environment set-up

To start an application from scratch we can use their generator:

1
npx create-next-app

We will be prompted for a name. I’ll use example for my app.

When the command finishes, we can start our app in development mode:

1
npm run dev

What we get out of the box:

  • Compilation and bundling (Babel and webpack)
  • Auto-refresh on changes
  • Static Site Generation (SSG) and Server Side Rendering (SSR)
  • Server of static files (Files under /public/)

Pages

The generator creates a /pages/ folder. In this folder there is a file named _app.js. This file defines the MyApp component, which is the entry point for an app:

1
2
3
4
5
6
7
import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

As we can see, the only thing it does is render a Component with some props. The Component attribute depends on the current route, we don’t need to worry about pageProps for now.

Next uses a file-system based router. This means that routes are created based on files inside /pages/. This means index.js will be rendered when the user navigates to /. When a user navigates to /contact, a file named contact.js will be used. If the file doesn’t exist a 404 page will be shown.

Pre-rendering

Next uses pre-rendering by default to make pages load fast. It supports 2 approaches: Static Site Generation and Server Side Rendering.

Static Site Generation refers to building an HTML page at compile time, and send this file as-is to users. This is the default mode. When we build a website in production mode, Next will compile the site and convert it into static pages. An example static page could be one that contains no dynamic elements:

1
2
3
export default function Contact() {
  return <div>Call me</div>
}

Static pages can also be created based on dynamic sources (A database, a service, etc.). The problem is that the page won’t be updated if the database is updated. This can be useful for pages that use information from a database, but we don’t expect the data to change often (If the data changes, we will need to re-build the website to get the new values). To build a static page based on dynamic data, we can use getStaticProps:

1
2
3
4
5
6
7
8
9
10
11
12
13
export default function Contact(props) {
  return <div>Call me: {props.phoneNumber}</div>
}

export async function getStaticProps() {
  // Here we could get information from a database, for example

  return {
    props: {
      phoneNumber: '213-344-2345'
    }
  }
}

This is there the pageProps in _app.js comes from.

The other alternative is Server Side Rendering (SSR). In this scenario, there won’t be HTML pages generated. Instead, the Next server will receive the request, will execute the JS code to get the necessary data and then will return the generated page. Since the server does this for every request, it’s slower. The benefit is that it will always return the latest information.

The only thing we need to do is use getServerSideProps instead:

1
2
3
4
5
6
7
8
9
10
11
12
13
export default function Contact(props) {
  return <div>Call me: {props.phoneNumber}</div>
}

export async function getServerSideProps() {
  // Here we could get information from a database, for example

  return {
    props: {
      phoneNumber: '213-344-2345'
    }
  }
}

There are a lot of options related to pre-rendering that we are not going to cover in this article. The documetation covers all these options.

Client side rendering

If for some reason pre-rendering doesn’t work for our use case we can always fall-back to client-side rending. This means Next will deliver some empty HTML skeleton and our code will be executed in the user’s browser to fetch the data needed to show that page.

Two main disadvantages of this approach are:

  • Not SEO (Search Engine Optimization) friendly
  • It takes longer for the user to see useful information

Client side rendering can be performed in many ways that are not specific to the Next framework. For example, we could detect when a component is rendered and fetch some data for it at that point.

Client side routing

After we have sent the initial page to the user (The landing page), we don’t need to do a full page refresh to transition between pages. We can link pages using the Link component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import Link from 'next/link'

export default function Contact(props) {
  return <div>
    <div>Call me: {props.phoneNumber}</div>
    <Link href="/">Go home</Link>
  </div>
}

export async function getServerSideProps() {
  // Here we could get information from a database, for example

  return {
    props: {
      phoneNumber: '213-344-2345'
    }
  }
}

When a Link is clicked, the framework fetches only what it needs to render the new page without refreshing the whole page.

CSS

In Next we can include CSS files from within our JS files using import. Styles imported from _app.js become global styles that can be used by any page. Our _app.js is already importing a stylesheet:

1
2
3
4
5
6
7
import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

The import path is relative to the current file.

Styles can also be included only for specific components. This approach allows us to style our components without having to worry about class name collisions. To style our Contact component, we will create a file named styles/Contact.module.css (Notice the naming convention: <ComponentName>.module.css):

1
2
3
.red {
  color: #f00;
}

We can now use the new styles:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Link from 'next/link'
import styles from '../styles/Contact.module.css'

export default function Contact(props) {
  return <div>
    <div className={styles.red}>Call me: {props.phoneNumber}</div>
    <Link href="/">Go home</Link>
  </div>
}

export async function getServerSideProps() {
  // Here we could get information from a database, for example

  return {
    props: {
      phoneNumber: '213-344-2345'
    }
  }
}

To apply the styles to an element, we use className={styles.red}.

Building for production

We have so far been running Next in “development” mode. To build a production version we can use:

1
npm run build

This command will create a .next folder with a production version of the app (Code minified, static pages generated, etc). We can then start the app:

1
npm run start

This way of running a Next app if good for scenarios that require Server Side Rendering for some pages.

If our application only needs static pages we can instead export it. We start by adding an export script to package.json:

1
2
3
4
5
6
7
8
{
  // Code omitted for brevity
  "scripts": {
    // Code omitted for brevity
    "export": "next export"
  },
  // Code omitted for brevity
}

We can now run the script (npm run build has to be run before):

1
npm run export

A folder named out will be created. This folder can then be deployed to a static server or a CDN.

Conclusion

Next seems like a nice tool to get started building a web application fast. It provides the necessary tooling out of the box, which is great for people who are not expert web developers.

On top of the ease of development, SSG and SSR helps with SEO and improve the user experience, which are usually problems people encounter when building web applications using React.

[ architecture  javascript  programming  react  ]
B-Trees - Database storage internals
Dependency injection (Inversion of Control) in Spring framework
Introduction to GraphQL
Building a Spring Boot server with Bazel
Introduction to Protocol Buffers and gRPC