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.
FYI: How do I create some kind of table of content in GitHub wiki?
- Project setup
- Deployment to GitHub Pages
- Adding Tailwind CSS to Create React App
- Formatting Code Automatically
- Adding Dark Mode with Tailwind CSS
- Pre-Rendering into Static HTML Files
- React auto generated README
- Available Scripts
- Learn More
- Acknowledgments
- Clone the repository with git.
git clone <url>- Install the dependencies to the local
node_modulesfolder (the directory will be automatically created). You will need NodeJS to runnpm. 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 installNOTE: as mentionned before, there might be some warnings. Don't try to fix them. Just follow to the next step.
- Start the server!
npm startNOTE: 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!
Before deploying to GitHub pages, ensure your branch is up to date and run
npm startto make sure everything works as expected!
Follow the official documentation for deploying to GitHub Pages from Create React App:
- Step 1:
Add homepagetopackage.json - Step 2: Install
gh-pagesand adddeploytoscriptsinpackage.json - Step 3: Deploy the site by running
npm run deploy - Step 4: For a project page, ensure your project’s settings use
gh-pages - Step 5: Optionally, configure the domain
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",
}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 installautoprefixer, because it is already installed. - Add Tailwind as a PostCSS plugin. Add
postcss.config.jsfile at the root of your project and paste the following code:
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}- Create your configuration file using
npx tailwindcss initand paste the following code in thetailwind.config.jsfile (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.cssfile and and use the@tailwinddirective to inject Tailwind'sbase,components, andutilitiesstyles:
@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
PostCSSconfiguration. A workaround, is to add the postCSS CLI npm package:npm install postcss-cli --save-devand to add the following scripts to yourpackage.jsonfile:
"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!
There is an official documentation on Formatting Code Automatically. Here are the steps:
npm install --save husky lint-staged prettier- 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,scssfromlint-staged. This will improve the running time because prettier will not format those files.
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 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.jsis defined as a class. - Tailwind UI docs React: great examples on how to implement Tailwind UI component into a React projects. The
DarkModeButtoncomponent 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 inApp.jsto return the values ofisDarkModeandisMenuOpenwhich 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.
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-helmetUpdate 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:
- General Error: Ok, so the error wasn't clear but after some digging I found out that I wasn't managing
404redirect, that is, an invalid request.react-snapwill generate a404.htmlfile. This file will be based on an invalid request to your website. For instance, let's say that you have a route for path/1and/2but 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 anundefinedvariable, and work a solution from there. Take a look at the<Switch>tag in./src/App.jsfor my solution. - 404 page title does not contain "404" string: Simply add
<title>404 - Page not found</title>to your404.htmlfile. And this is why we needreact-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 importreact-helmetand define thetitlehtml 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.
This project was bootstrapped with Create React App.
In the project directory, you can run:
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.
Launches the test runner in the interactive watch mode.
See the section about running tests for more information.
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.
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.
You can learn more in the Create React App documentation.
To learn React, check out the React documentation.
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
- Create React App
- Tailwind CSS
- Icons from Heroicons
- react-snap
- And all the other amazing dependencies referenced in this README.