Builder Book logo

Book: Builder Book

  1. Introduction
  2. App structure. Next.js. HOC. Material-UI. Server-side rendering. Styles.
  3. Server. Database. Session. Header and MenuDrop components.
  4. Authentication HOC. Promise. Async/await. Static method for User model. Google OAuth.
  5. Testing with Jest. Transactional emails with AWS SES API. In-app notifications.
  6. Book and Chapter data models. Internal API infrastructure, API methods and Express routes. ReadChapter page.
  7. Github integration. Admin dashboard. Testing Admin UX and Github integration.
  8. Table of Contents. Highlight for section. Hide Header. Mobile browser.
  9. BuyButton component. Stripe API. Checkout flow. MyBooks page. Mailchimp API.
  10. Environmental variables, production/development. Logger. SEO, robots.txt, sitemap.xml. Google Analytics. Compression. Security. Heroku. Testing deployed project. AWS Elastic Beanstalk.

Chapter 1: App structure. Next.js. HOC. Material-UI. Server-side rendering. Styles.

The section below is a preview of Builder Book. To read the full text, you will need to purchase the book.

We are currently refactoring and updating the code in Builder Book. To reduce confusion, we have temporarily disabled the buy button. You can follow our progress here:

You can still buy our second book, SaaS Boilerplate:

In Chapter 1, you'll start with the codebase in the 1-begin folder of our builderbook repo and end up with the codebase in the 1-end folder. We'll cover the following topics in this chapter:

  • Setup
    - Node and Yarn
    - package.json

  • Code editor and lint
    - VS editor
    - Eslint
    - Prettier

  • App structure
    - Next.js
    - Document
    - Project structure

  • Index page

  • Header component

  • Layout HOC

  • Material-UI integration
    - Add styles on server
    - Remove server-side styles

  • Server-side rendering

You've read about the motivation for writing this book and building a web application. Motivation aside, this book will teach you how to build a modern-stack, production-ready web application from scratch. Together, we will go from 0 to nearly 4,000 lines of code in 9 chapters of this book.

In this very first chapter, we have multiple goals:

  • set up our coding environment
  • create our app structure
  • get familiar with Next.js (Next)
  • create our first page and component
  • create a higher-order component
  • integrate Next with Material-UI
  • learn about server-side rendering
  • add global and shared styles

link Setup

We work on Ubuntu 16.04 LTS. We skipped the 17.04 release and decided to wait for stable 18.04. Thus, we provide installation instructions specific to a Linux-based OS (for example, Ubuntu and MacOS). we use the Visual Studio editor (VS editor), which we find easier to use and automate than any other popular editor. The web application that we build in this book will allow you, via integration with Github, to use VS editor for writing documentation and books.

The core technologies of the Builder Book app are React, Material-UI, Next, Express, Mongoose, and MongoDB. By the end of this chapter, we will create a static web app (no server and no database yet). To do so, we will install and integrate the first three technologies - React, Material-UI, and Next.

link Node and Yarn

We are building a Node app, and many of the tools that we use in this app also require Node.

I suggest using Node with the help of nvm (Node Version Manager). On Linux, press Ctrl+Alt+T to open your terminal (alternatively, use the search bar to search for terminal).

  • Run the command below to install nvm:
    curl -o- | bash

  • Check the nvm version to confirm successful installation:
    nvm --version

  • Trigger nvm:
    . ~/.nvm/

  • Install Node 12.14.0:
    nvm install 12.14.0

  • Make it default:
    nvm alias default 12.14.0

  • Check Node version to confirm successful installation:
    node -v

Node version should be 12.14.0.

Once Node is installed, we can install Yarn, a manager for third-party packages (also called dependencies or modules). Whenever we need to use code developed by other developers - we add the package name and version to a package.json file and run yarn in the app's directory. More on package.json in the next section.

If you're using Ubuntu:

  • Configure the Debian package repository for Yarn by running the following two commands in your terminal:
    curl -sS | sudo apt-key add -
    echo "deb stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
  • Then install Yarn with:
    sudo apt-get update && sudo apt-get install yarn
  • Check Yarn version to confirm successful installation:
    yarn -v

If you're using another operating system, find specific instructions here on yarn's official website. Select your OS from the "Operating system" dropdown menu.

In this book, the Yarn version is 1.7.1.

link package.json

package.json is a required file at the root of any Node application. This file contains the app's metadata: app name, app version, scripts, and dependenices (described by name and version), among other properties. Read about working with a package.json file to learn more about metadata. Some parameters are optional (keywords, license), but some are required (name, version).

Open the 1-begin folder located at builderbook/book/. Take a look at our package.json file:
package.json :

    "name": "1-begin",
    "version": "0.0.1",
    "license": "MIT",
    "scripts": {
        "dev": "next"
        "build": "next build",
        "start": "next start",
    "dependencies": {
        "@material-ui/core": "4.8.0",
        "@material-ui/styles": "4.7.1",
        "next": "9.1.6",
        "prop-types": "^15.7.2",
        "react": "^16.12.0",
        "react-dom": "^16.12.0",
    "devDependencies": {}

You can see required metadata like name and version (version format is major.minor.patch).

The section of package.json called scripts contains shortcuts for commands. At the end of this book, the scripts section will contain the following command:

"dev": "nodemon server/server.js --watch server"

This command (shortcut) allows us to run our app locally by typing yarn dev in the terminal instead of manually typing:

yarn nodemon server/server.js --watch server

It's ok if you don't understand the above command. We will discuss it detail in Chapter 2 when we write our first server-side code. In this chapter and part of Chapter 2, since there is no server to start, you will run your app locally with yarn next or yarn dev ("dev": "next").

Also in Chapter 2, we'll introduce the dotenv package, which will manage most of our environmental variables (over a dozen in total) in our app.

Inside the scripts commands, you may choose to pass environmental variables. You would set it up by adding NODE_ENV=production to one of the commands in the scripts section. Let's consider an example. At the end of this book, we will start our app locally using yarn dev, which has the following shortcut in the scripts section:

"dev": "nodemon server/server.js --watch server"

We don't pass any environmental variables inside this command. Thus, NODE_ENV and ROOT_URL default to development and http://localhost:8000, respectively.

We are able to pass environmental variables by prepending them to the command. For example, to build our app on a remote production server, we may use yarn build with this shortcut:

"build": "NODE_ENV=production next build"

In this case, the value of NODE_ENV is explicitly set to production. Next.js will build the production app properly with NODE_ENV equal to production. In Chapter 2, we will discuss how to manage server-side environmental variables. In Chapter 8, we will discuss how to manage universally available (available on client and server) environmental variables.

The next section in our package.json file is dependencies. This section contains a list of third-party packages that we need in production and development environments. To install all packages from package.json, simply run yarn in your terminal while inside the app's directory.

To check if Yarn successfully installed the packages, look for a newly generated node_modules folder and yarn.lock lockfile at the app's root directory. The former folder contains the code of third-party packages, and the latter file contains the exact versions of packages and their dependencies.

In the scripts section, you can specify a combination of commands or run the scripts directly from your files. From our package.json file, if you run yarn dev in your terminal, you will first run yarn next.

devDependencies are dependencies that our app uses in development but not in production. Typically, developers use packages in devDependencies to run tests, compile code, or lint code locally.

If you ran the yarn command inside the 1-begin folder, then you successfully installed all packages we need for Chapter 1. We will discuss each installed package later in this chapter.

link Code editor and lint

In this section, we will set up preferences for Visual Studio Code editor (VS editor) and discuss/install two extensions that help us format code: Eslint and Prettier.

link VS editor

I prefer the Visual Studio code editor, because it easily integrates with Github and Eslint (which formats code), and it comes with Terminal (this allows you to stay within the editor while running commands). Here is a typical view of the editor with terminal. A list of staged changes is one click away, and you can see that we have 2 staged changes:
Builder Book

In addition, VS editor has many optional settings that may increase your productivity. VS editor has 2 types of settings: User settings and Workspace settings. Read more in Visual Studio's docs.

The User settings are specific to your machine and will apply globally to any project you work on unless you configure Workspace settings. Since we only want to create settings for our builder book project, and not affect any settings you'd like on your own machine, we will modify the Workspace settings.

You can access Workspace Settings by going to File(or Code)>Preferences>Settings>Workspace Settings. Then click the left icon at the top right of your screen to select Open Settings (JSON). Here is a list of our Workspace Settings:

    "window.zoomLevel": -1,
    "files.autoSave": "afterDelay",
    "git.enableSmartCommit": true,
    "editor.formatOnSave": true,
    "editor.codeActionsOnsSave": {
        "source.fixAll.eslint": true
  • "window.zoomLevel": -1 sets the zoom level to 80% - our personal preference, since we like to scroll less, especially when writing a tutorial or book.
  • "files.autoSave": "afterDelay" saves files automatically for you, so you don't need to save your files manually.
  • "git.enableSmartCommit": true stages changes automatically, so you don't need to stage your changes manually.
  • "editor.codeActionsOnSave": {"source.fixAll.eslint": true} detects formatters, such as Eslint, and formats your code on each save event. To format code on a save event, you have to manually save the file.

Below, we will discuss the remaining setting that is related to the ESLint code formatter.

To format code, we use the ESLint extension by Dirk Baeumer in Visual Studio. We configure this extension with dependencies for Prettier by Esben Petersen:

link Eslint and Prettier

Eslint lints code, i.e. it checks code for potential formatting problems and fixes them. After you install the Eslint extension on VS editor, you will see new settings. Read the full list of settings here. One setting that we want to set to true is:

"eslint.autoFixOnSave": true

This setting makes Eslint apply fixes to your code when you save a file manually.

Prettier is also a code formatter. Rather than install the Prettier extension in VS editor, we will use Prettier installed as a dev dependency with Eslint. We chose this set up because it is compatible with VS Editor, Sublime Text 3, and Atom, in case you prefer one of the latter two code editors over VS Editor.

Before we jump in and test Eslint and Prettier out, we need to create a configuration file and install missing dependencies. Eslint requires a .eslintrc.js file that contains a list of formatting rules. Formatting rules can specify the type of quotes and commas to use, the maximum length for a line of code, and more. Our .eslintrc.js file is at the root of our builder book directory, and all book chapter codes inherit the formatting specified in this file.
.eslintrc.js :

module.exports = {
    parser: 'babel-eslint',
    extends: ['airbnb', 'plugin:prettier/recommended'],
    env: {
        browser: true,
        jest: true,
    plugins: ['react', 'jsx-a11y', 'import', 'prettier'],
    rules: {
        'max-len': ['error', 100],
        'no-underscore-dangle': ['error', { allow: ['_id'] }],
        'no-mixed-operators': 'off',
        'prefer-destructuring': [
                VariableDeclarator: {
                    array: false,
                    object: true,
                AssignmentExpression: {
                    array: true,
                    object: false,
                enforceForRenamedProperties: false,
        'import/prefer-default-export': 'off',
        'jsx-a11y/anchor-is-valid': 'off',
        'react/react-in-jsx-scope': 'off',
        'react/jsx-filename-extension': [
                extensions: ['.jsx'],
        'prefer-arrow-callback': 'error',
        'prettier/prettier': [
                singleQuote: true,
                trailingComma: 'all',
                arrowParens: 'always',
                printWidth: 100,

We included the .eslintrc.js file with all necessary rules in the main builder book folder.

To make Eslint and Prettier work properly, we need to install the missing packages that they rely on.

Add the following dependencies to your package.json file and run yarn:

"devDependencies": {
    "babel-eslint": "10.0.3",
    "eslint": "6.8.0",
    "eslint-config-airbnb": "18.0.1",
    "eslint-config-prettier": "6.7.0",
    "eslint-plugin-import": "2.19.1",
    "eslint-plugin-jsx-a11y": "6.2.3",
    "eslint-plugin-prettier": "3.1.2",
    "eslint-plugin-react": "7.17.0",
    "eslint-plugin-react-hooks": "2.3.0",
    "prettier": "1.19.1"

A list of all Eslint rules is in the official docs. Check out, for example, the max-len rule; though note that the official example specifies a max-len of 80, while we are specifying a max-len of 100 (just personal preference).

You may have noticed that we installed the eslint-config-airbnb package and extended Eslint with extends: 'airbnb'. In addition to rules that we specified in .eslintrc.js, we use rules from the eslint-config-airbnb package.

Now that you've properly installed and configured Eslint and Prettier, let's test them. We'll start with testing Eslint.

Create a pages/index.jsx file inside your 1-begin folder, then paste the following code:
pages/index.jsx :

[1, 2, 3].map(function (x) {
    const y = x + 1;
    return x * y;

Eslint will highlight this code, and when you hover over the highlighted code, you will see a description of the error(s):

[eslint] Unexpected unnamed function. (func-names)
[eslint] Unexpected function expression. (prefer-arrow-callback)

This type of Eslint error is a result of Airbnb's format rule for arrow functions. To fix this error, simply save the file manually with Ctrl+S, and the code will be edited into:
pages/index.jsx :

[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;

You've reached the end of the Chapter 1 preview. To continue reading, you will need to purchase the book.

We are currently refactoring and updating the code in Builder Book. To reduce confusion, we have temporarily disabled the buy button. You can follow our progress here:

You can still buy our second book, SaaS Boilerplate: