Demo website: modokemdev.com/daily-organizer
This repository was built to deploy a Daily Organizer App to GitHub Pages using gh-pages. This app uses React and Redux to display components. Routing determines which component to display.
This README focuses on deploying a React app to to GitHub Pages. For more details on the project, take a look at my GitHub repository express-react-app.
To run this project locally, clone the project and run:
# install node_modules
$ npm install
# run the project
$ npm run dev
# deploy to gh-pages (use git bash)
$ npm run deploy
NOTE: the following steps allow you to set up a project from scratch. The output is very similar to what you would get with Create React App. The only difference is that you are going to understand the magic behind Create React App.
# Generate package.json file
$ npm init --yes
# Install Webpack and related dependencies
$ npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin
# Install Babel and related dependencies
$ npm install --save-dev @babel/core @babel/node @babel/preset-env @babel/preset-react @babel/register babel-loader
# Install React and related dependencies
$ npm install --save react react-dom react-redux
# Add .gitignore file
$ printf '%s\n' 'node_nodules' 'dist' > ./gitignore
NOTE: for more info on
printf
, see this answer in Stack Exchange.
- Add a
.babelrc
file:
{
"presets": [
["@babel/preset-env",{
"targets":{
"node":"current"
}
}],
"@babel/preset-react"
]
}
- Add a
webpack.config.js
file:
const path = require("path");
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, 'src','app'),
plugins: [
new HtmlWebpackPlugin({
template: 'index.html',
inject: false
})
],
output: {
path: path.resolve(__dirname,'dist'),
filename: 'bundle.js',
publicPath: '/',
},
resolve: {
extensions: ['.js','.jsx']
},
devServer: {
historyApiFallback: true,
port: 8080,
host: 'localhost',
open: true
},
module: {
rules: [{
test: /\.jsx?/,
loader:'babel-loader'
}]
}
}
- Add a simple
Main.jsx
component atsrc/app/components/Main.jsx
:
import React from 'react';
const Main = () => {
return <div>Daily Organizer</div>;
};
export default Main;
- Add the
index.jsx
entry file atsrc/app/index.jsx
:
import React from 'react';
import ReactDOM from 'react-dom';
import Main from './components/Main';
ReactDOM.render(
<Main />,
document.getElementById('app')
);
- Add an
index.html
file:
NOTE: You can manually add the
index.html
file to thedist
folder at the root of the project but it is advised to setup html-webpack-plugin. See the getting started tutorial from Webpack for more info.
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<title>Daily Organizer</title>
</head>
<body class="container">
<div id="app"></div>
<script src="/bundle.js"></script>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
</body>
</html>
NOTE: We use BootstrapCDN for this project.
- Add the following script to
package.json
:
{
"scripts": {
"dev": "webpack serve"
},
}
- Run the application:
# start webpack-dev-server
$ npm run dev
Because we specify the
open: true
option inwebpack.config.js
, your browser should open automatically. If not, navigate to http://localhost:8080/. You should seeDaily Organizer
displayed on the top left of the screen.
This web application is ready for deployment using gh-pages. Simply run the following command in git bash
: npm run deploy
.
For this to work, you previously need to install gh-pages (npm install gh-pages --save-dev
) and update the package.json
scripts:
{
"scripts": {
"build": "webpack --config webpack.prod.js",
"deploy": "npm run build && gh-pages -d build"
}
}
You will also need to update webpack.config.js
and index.html
.
- Install HtmlWebpackPlugin (
npm install --save-dev html-webpack-plugin
) and adjust thewebpack.config.js
file. - Install clean-webpack-plugin (
npm install --save-dev clean-webpack-plugin
) and configure it.
Finally, because we are deploying to https://<USERNAME>.github.io/<REPO>
, you will need to add a webpack.prod.js
file at the root of the project. This is the easiest way I found to tell Webpack to prefix the /bundle.js
path in index.html
with the repo
.
- Inserting variables into HTML and JavaScript with Webpack - This explains why we add a
<%= htmlWebpackPlugin.options.repo %>
toindex.html
. - HTML Webpack Plugin - Check the Options and the Writing Your Own Templates. It helps to understand the plugin use cases.
- Webpack Configuration.
- Webpack Output Management.
- Webpack HtmlWebpackPlugin.
- Webpack Development.
- Webpack Production.
NOTE: take a close look at the
REPO
variable in the code below. TheREPO
variable is necessary because we are deploying tohttps://<USERNAME>.github.io/<REPO>
. For this to work, we need to add the publicPath configuration option and the DefinePlugin variableREPO
inwebpack.config.js
andwebpack.prod.js
. Be sure to take a look at those files!.
- Add react-router-dom:
npm install react-router-dom --save
. - Add a new
Navigation
component atsrc/app/components/Navigation.jsx
:
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import React from 'react';
const Navigation = () => (
<div className="header">
<Link to={`${REPO}/dashboard`}>
<h1>
Daily Organizer
</h1>
</Link>
</div>
);
export const ConnectedNavigation = connect(state=>state)(Navigation);
- Update
Main.jsx
to import theNavigation
component andBrowserRouter
andRoute
fromreact-router-dom
:
import React from 'react';
import { Provider } from 'react-redux';
import { store } from '../store';
import { ConnectedDashboard } from './Dashboard';
import { BrowserRouter, Route, } from 'react-router-dom';
import { ConnectedNavigation } from './Navigation';
import { ConnectedTaskDetail} from './TaskDetail';
const Main = () => {
return (
<BrowserRouter>
<Provider store={store}>
<div className="container mt-3">
<ConnectedNavigation/>
<Route
exact
path={`${REPO}/dashboard`}
render={ () => (<ConnectedDashboard/>)}
/>
<Route
exact
path={`${REPO}/task/:id`}
render={ ({ match }) => (<ConnectedTaskDetail match={ match }/>)}
/>
</div>
</Provider>
</BrowserRouter>
);
};
export default Main;
For more info, take a look at my original repo: express-react-app.