Skip to content

marcoandre1/marcoandre1.github.io

Repository files navigation

ModokemDev Build and deploy pages-build-deployment

modokemdev.com is my personal website. As of December 2020, this website is built with Create React App and Tailwind CSS. This README explains some of the steps required to have a working website on GitHub Pages. If you have any question, please open an issue or start a discussion. Thank you!

NOTE: to any one starting a new project, please consider Next.js. The main reason I use Create React App (CRA) is because this is an old repo and CRA was, at the time, a good solution. But Next.js is a newer, easier and far better solution to implement a React website. Take a look at my Next.js repositories speakers-app and nextjs-blog, here on GitHub. That said, CRA apps are still very good and probably better for beginners mainly because there is less magic happening than in a Next.js app. It really depends on your use case.

Table of contents

FYI: How do I create some kind of table of content in GitHub wiki?

Project setup

  • Clone the repository with git.
git clone <url>
  • Install the dependencies to the local node_modules folder (the directory will be automatically created). You will need NodeJS to run npm. If you are on windows here is a good tutorial, just make sure to run everything on admin mode to avoid errors: Install NodeJS on Windows.
npm install

NOTE: as mentionned before, there might be some warnings. Don't try to fix them. Just follow to the next step.

  • Start the server!
npm start

NOTE: if it's your first time running npm, you might get a prompt from firewall asking to allow local node server. Obviously you need to allow it!

Deployment to GitHub Pages

Before deploying to GitHub pages, ensure your branch is up to date and run npm start to make sure everything works as expected!

Follow the official documentation for deploying to GitHub Pages from Create React App:

  1. Step 1: Add homepage to package.json
  2. Step 2: Install gh-pages and add deploy to scripts in package.json
  3. Step 3: Deploy the site by running npm run deploy
  4. Step 4: For a project page, ensure your project’s settings use gh-pages
  5. Step 5: Optionally, configure the domain

Additional notes for deploying to GitHub Pages

Make sure to run npm run deploy from git bash (just tap git bash on the console, it should already be installed if you have git). This is necessary because only git bash is going to prompt you for your password, if you have set your ssh key, which is necessary to deploy to GitHub Pages. If you want to learn more about git bash you can check What is Git Bash for Windows anyway?.

Also, if you don't want to add manually the CNAME file after deploying, you can add it on the build process. Update the package.json scripts as follows:

"scripts": {
  "predeploy": "npm run build",
  "deploy": "gh-pages -d build",
  "build": "react-scripts build && echo www.modokemdev.com > ./build/CNAME",
}

Adding Tailwind CSS to Create React App

NOTE: As of December 2020, there is an official guide to Install Tailwind CSS with Create React App. You can either follow the official documentation or the steps below.

The main reason why adding Tailwind CSS to Create React App (CRA) is a problem is that CRA manages the WebPack config for us. There are plenty of ways to add Tailwind CSS. You can add the CRACO npm package (see dev.to article and this Create React App issue). Another way of adding Tailwind CSS without CRACO is explained in this article : create-react-app with tailwind via postcss plus purgecss, which is close to what I did.

Personally, I followed this article : Setup Tailwind with PostCSS in Create-React-App in 5 Minutes, and the Tailwind official installation guide.

Here are the steps :

  • Following Tailwind official installation guide, install Tailwind via npm: npm install tailwindcss. You don't need to install autoprefixer, because it is already installed.
  • Add Tailwind as a PostCSS plugin. Add postcss.config.js file at the root of your project and paste the following code:
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}
  • Create your configuration file using npx tailwindcss init and paste the following code in the tailwind.config.js file (you can also manually create the file, it is located at the root of the project):
module.exports = {
  future: {
    removeDeprecatedGapUtilities: true,
    purgeLayersByDefault: true,
  },
  purge: ['./src/**/*.{js,ts,jsx,tsx}', './public/index.html'],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {
      colors: {
        'accent-1': '#333',
      },
    },
  },
  variants: {
    extend: {},
  },
  plugins: [],
}
  • Include Tailwind in your CSS. Add a src/styles/index.css file and and use the @tailwind directive to inject Tailwind's base, components, and utilities styles:
@tailwind base;
@tailwind components;
@tailwind utilities;
  • Build your CSS. This is where it gets tricky because we can't tell WebPack by default how to pick PostCSS configuration. A workaround, is to add the postCSS CLI npm package: npm install postcss-cli --save-dev and to add the following scripts to your package.json file:
"scripts": {
  "build:styles": "postcss src/styles/index.css -o src/index.css",
  "prebuild": "npm run build:styles",
  "prestart": "npm run build:styles"
  }

You don't need to install postCSS because it is already installed.

For this to work, you will need to keep the import directive for index.css in the src/index.js file:

import './index.css';

Now, every time you run your project, as you would usually do, an index.css file will be generated in the src folder containing your tailwind CSS!

Formatting Code Automatically

There is an official documentation on Formatting Code Automatically. Here are the steps:

  1. npm install --save husky lint-staged prettier
  2. Update package.json:
{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "src/**/*.{js,jsx,ts,tsx,json,md}": [
      "prettier --write"
    ]
  }
}

Note: Because we are using Tailwind CSS, we recommend removing css,scss from lint-staged. This will improve the running time because prettier will not format those files.

Adding Dark Mode with Tailwind CSS

Tailwind CSS offers the possibility to add dark mode. All you need to do is update the darkMode setting in your tailwind.config.js file from false to media or class. In our case, I added class because it allows us to toggle dark mode manually.

// tailwind.config.js
module.exports = {
  darkMode: 'class',
  // ...
}

To enable dark mode with class you need to add the dark class to the html tag:

<!-- Dark mode enabled -->
<html class="dark">
<body>
  <!-- Will be black -->
  <div class="bg-white dark:bg-black">
    <!-- ... -->
  </div>
</body>
</html>

Now, this is where it becomes tricky because you need to manipulate a DOM element, the html tag, with React. There is not a good way of doing this. Which means that you can do it the way you want because React does not offer a straightforward solution for doing this. Following the Tailwind CSS docs for dark mode, I implemented a solution which works quite well without adding any extra package. Please, take a look at App.js to see how this is done and the section below for further details.

Toggling Dark Mode (extra notes)

Toggling dark mode with React and Tailwind CSS can be a pain if you are not very familiar with either of one (my case). Keep in mind that Tailwind CSS docs don't offer a solution to implement this feature but rather guidelines. You really need to understand or try your best at understanding React and Tailwind CSS to get a nice result.

Here are some of the articles that helped me to implement the final solution:

  • How to Use Variables within Classes: good starting point at understanding variables within classes, which is my case because App.js is defined as a class.
  • Tailwind UI docs React: great examples on how to implement Tailwind UI component into a React projects. The DarkModeButton component is based on the basic click handler demo.
  • Adding Lifecycle Methods to a Class: shows how to use this.setState() to apply updates to the component local state. I use it in App.js to return the values of isDarkMode and isMenuOpen which are Booleans that control CSS properties dynamically.
  • componentDidMount(): is invoked immediately after a component is mounted. This comes particularly handy for defining the initial dark mode state.

Pre-Rendering into Static HTML Files

NOTE: CRA docs has a section on deploying to GitHub Pages that includes Notes on client-side routing. It offers some hacks to add a router to a project hosted on GitHub Pages. This section, Pre-Rendering into Static HTML Files, will help you with that but alternatively you can also take a look at spa-github-pages.

CRA offers some guidelines on implementing Pre-Rendering into Static HTML Files and a link to a zero-configuration pre-rendering tutorial.

First of all, remember that this website is meant to be deployed on GitHub Pages. As counter intuitive as it may sound, deploying a complex website to GitHub Pages, for instance a website with more than one route, is not a straightforward task.

Why do we even care about generating static HTML files? Well, in case you don't know, static generation can improve your website loading time because you are rendering HTML files for every route. The problem is that React uses client-side JavaScript to populate data. This can be slow if you have lots of data to load or worst, your JavaScript bundle can just fail, in which case your page is not going to load. You can read a bit more at When to Use Static Generation v.s. Server-side Rendering. Now, if it would have been just for this reason, I wouldn't have add static generation because I feel it's useless in our case. However, ...

Static Generation is the only way to correctly add a complex website (more than one route) to GitHub Pages.

Adding static generation for CRA is easy. Simply add react-snap and react-helmet to your project:

npm install --save-dev react-snap
npm install --save react-helmet

Update package.json:

"scripts": {
  "postbuild": "react-snap"
}

Update src/index.js:

import { hydrate, render } from "react-dom";

const rootElement = document.getElementById("root");
if (rootElement.hasChildNodes()) {
  hydrate(<App />, rootElement);
} else {
  render(<App />, rootElement);
}

That's it! You can now throw a build and see the generated files. Wait, if that's all, why did we install react-helmet?!

Well, the reason is that react-snap was last published on December 13, 2018. Here is the repo on GitHub and the npm package page. Dependencies being outdated, you might get some errors while building your website with react-snap. Here are 2 errors I encountered and how I managed them:

  1. General Error: Ok, so the error wasn't clear but after some digging I found out that I wasn't managing 404 redirect, that is, an invalid request. react-snap will generate a 404.html file. This file will be based on an invalid request to your website. For instance, let's say that you have a route for path /1 and /2 but not for /3, how is you CRA app managing 404 page not found? Is it throwing an error? If that's the case, you need to manage it. Easiest way of doing it, is to catch the error, maybe an undefined variable, and work a solution from there. Take a look at the <Switch> tag in ./src/App.js for my solution.
  2. 404 page title does not contain "404" string: Simply add <title>404 - Page not found</title> to your 404.html file. And this is why we need react-helmet. Keep in mind that there are probably other ways of doing this but it seems a good approach globally. For this to work, simply update your react component to import react-helmet and define the title html tag:
import { Helmet } from "react-helmet";

class App extends Component {
  render () {
    return (
        <div>
            <Helmet>
                <title>404 - Page not found</title>
            </Helmet>
        </div>
    );
  }
};

Note: take a look at my Dashboard component to see how I implemented it.

React auto generated README

This project was bootstrapped with Create React App.

Available Scripts

In the project directory, you can run:

npm start

Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.

The page will reload if you make edits.
You will also see any lint errors in the console.

npm test

Launches the test runner in the interactive watch mode.
See the section about running tests for more information.

npm run build

Builds the app for production to the build folder.
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.
Your app is ready to be deployed!

See the section about deployment for more information.

npm run eject

Note: this is a one-way operation. Once you eject, you can’t go back!

If you aren’t satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project.

Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc.) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.

You don’t have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.

Learn More

You can learn more in the Create React App documentation.

To learn React, check out the React documentation.

Code Splitting

This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting

Analyzing the Bundle Size

This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size

Making a Progressive Web App

This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app

Advanced Configuration

This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration

Deployment

This section has moved here: https://facebook.github.io/create-react-app/docs/deployment

npm run build fails to minify

This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

Acknowledgments