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_modules
folder (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 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!
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:
- Step 1:
Add homepage
topackage.json
- Step 2: Install
gh-pages
and adddeploy
toscripts
inpackage.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.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 thetailwind.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'sbase
,components
, andutilities
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 yourpackage.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!
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,scss
fromlint-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.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 inApp.js
to return the values ofisDarkMode
andisMenuOpen
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.
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:
- 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 a404.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 anundefined
variable, and work a solution from there. Take a look at the<Switch>
tag in./src/App.js
for my solution. - 404 page title does not contain "404" string: Simply add
<title>404 - Page not found</title>
to your404.html
file. 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-helmet
and define thetitle
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.
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.