The website of the Autonomous Intelligent Robotics (AiR) Laboratory at LSU New Orleans. It is a Create React App single-page application built with MUI and React Router, deployed as a static site on GitHub Pages.
Live site: https://redwannewaz.github.io/
Repository: git@github.com:RedwanNewaz/RedwanNewaz.github.io.git
- Running the site locally
- Hosting on GitHub Pages
- Why a single-page app needs the 404 trick
- Serving from a different URL
- Editing the content
- Troubleshooting
You need Node.js 18 or newer (which includes npm).
git clone git@github.com:RedwanNewaz/RedwanNewaz.github.io.git
cd RedwanNewaz.github.io
npm install # first time only
npm startThe dev server prints a URL, usually http://localhost:3000. It reloads on save.
To check what will actually ship, build the production bundle and serve it:
npm run build
npx serve -s build # then open http://localhost:3000The -s flag matters: it makes the local server fall back to index.html for
unknown paths, which is what lets /publication and /funding load directly.
GitHub Pages does not do this, which is what section 3 is about.
The repository is a user site (RedwanNewaz.github.io), so GitHub serves it
at the domain root: https://redwannewaz.github.io/. Source code lives on the
working branch; the compiled site lives on a separate gh-pages branch. You
never commit the build/ folder to the source branch — .gitignore excludes
it.
If the live site shows this README instead of the website, skip to the one setting that causes it.
A workflow is already committed at .github/workflows/deploy.yml. On every
push to main, master, or redwan it installs dependencies, runs
npm run build, and uploads build/ to GitHub Pages as a deployment artifact.
One-time setup:
- Push this repository to GitHub, including the
.github/folder. - Go to Settings → Pages.
- Under Build and deployment → Source, choose GitHub Actions. Not "Deploy from a branch" — see below for why.
- Push a commit, or open the Actions tab and run Build and deploy to GitHub Pages via Run workflow.
The first deploy takes a couple of minutes. After that, every push republishes. The Actions tab shows the log, and the run summary links to the deployed URL.
If your default branch is not one of main, master, or redwan, add its
name to the branches: list at the top of .github/workflows/deploy.yml.
Symptom: you visit the live site and see this README rendered as a web page, often with the repository name as the heading — no navbar, no styling.
Cause: Settings → Pages → Source is set to Deploy from a branch
pointing at the branch that holds the source code. That branch has no
index.html at its root, so GitHub falls back to rendering README.md with
Jekyll. It is publishing the repository, not the website.
Fix: set Source to GitHub Actions and re-run the workflow. Nothing in the code needs to change.
The underlying point: the branch you write code on and the thing GitHub
serves are different. src/ is not a website — it only becomes one after
npm run build turns it into build/. Pages has to be pointed at the build
output, whether that arrives as an Actions artifact (Option A) or on a
gh-pages branch (Option B).
The gh-pages package and the
matching scripts are already in package.json:
"scripts": {
"predeploy": "npm run build",
"deploy": "gh-pages -b gh-pages -d build"
}To publish:
npm install # once, to pull in the gh-pages dev dependency
npm run deploypredeploy runs the build automatically, then gh-pages commits build/ to
the gh-pages branch and pushes it.
This route needs Settings → Pages → Source set to Deploy from a branch →
gh-pages → / (root). That is mutually exclusive with Option A, so
pick one and stay with it. Mixing them is the usual reason a site reverts to an
old version: a stale gh-pages branch left over from an earlier setup gets
served instead of the current build.
npm run build produces a build/ folder containing index.html, 404.html,
hashed JS/CSS bundles under static/, and everything from public/. That
folder is the entire website — there is no server-side component, no database,
and no build step on GitHub's side beyond what the workflow does.
public/.nojekyll is copied through to build/. It tells GitHub Pages to
serve the files as-is instead of running them through Jekyll, which would
otherwise ignore any path beginning with an underscore.
This is the one genuinely surprising part of hosting a React Router site on GitHub Pages, so it is worth understanding before you change anything.
React Router handles /research, /publication, /funding and the rest in
the browser. There are no such folders on disk — only index.html. When a
visitor clicks a link inside the site, nothing is requested from the server and
everything works.
But when someone pastes https://redwannewaz.github.io/funding into the
address bar, presses reload on that page, or follows an external link to it,
the browser asks GitHub Pages for a file at /funding. That file does not
exist, so Pages returns its 404 page. A traditional host would be configured to
fall back to index.html; GitHub Pages offers no such setting.
The workaround, from rafgraph/spa-github-pages, is split across two files that are already in this repository:
public/404.html— served by GitHub whenever a path is not found. A small script rewrites the requested path into a query string and redirects to the site root, e.g./fundingbecomes/?/funding.public/index.html— a matching snippet in<head>detects that query string and useshistory.replaceStateto put the real path back in the address bar. It runs before React boots, so React Router sees/fundingand renders the right page.
The visitor sees a brief redirect and lands on the correct page with a clean URL. Do not delete either script, and keep them in sync if you move the site.
If a deep link ever 404s after a change, that pair is the first thing to check.
Two settings must agree with wherever the site is served from.
| Where the site lives | homepage in package.json |
pathSegmentsToKeep in public/404.html |
|---|---|---|
https://redwannewaz.github.io/ (current) |
"/" |
0 |
https://redwannewaz.github.io/airlab/ (project page) |
"https://redwannewaz.github.io/airlab" |
1 |
A custom domain at its root, e.g. https://airlab.example.edu/ |
"/" |
0 |
homepage tells the build where to point asset URLs. Get it wrong on a
subpath and you get a blank page with 404s for the JS and CSS bundles in the
browser console. pathSegmentsToKeep tells the 404 script how much of the path
is the site's base rather than a route.
-
Create a file
public/CNAMEcontaining only the hostname, no protocol and no trailing slash:airlab.example.eduPutting it in
public/means every build copies it intobuild/, so the setting survives redeploys. (Setting the domain only in the Pages UI works until the next deploy wipes it.) -
Ask whoever runs your DNS to add a
CNAMErecord pointing your hostname atredwannewaz.github.io. -
In Settings → Pages, confirm the custom domain is listed and tick Enforce HTTPS once the certificate is issued (this can take an hour).
Almost all text lives in src/constants/data/. You can update the site without
touching a React component.
| File | What it controls |
|---|---|
heroImageData.js |
The title and subtitle in the banner at the top of every page |
homeData.js |
Home page lead text, stat tiles, research thrusts, robot platform cards |
newsData.js |
The News page and the "Top News" box on the home page |
publicationData.js |
The Publications page (auto-generated — see below) |
researchData.js |
Research area cards and their detail pages |
projectData.js |
The Projects page (project websites) |
fundingData.js |
The Funding page: funded projects, equipment grants, collaborations |
advisingData.js |
The Join Us page |
teamData.js |
Team member cards |
contactData.js |
Contact cards |
linkData.js |
Profile links in the footer and on Contact, plus the lab address |
videoData.js |
The Videos page |
General rules:
- Every
idwithin an array must be unique. Ids are used as React keys and to cross-link research areas to publications. - Do not rename the keys inside an object; the components read them by name.
- Images belong in
src/assets/images/, logos insrc/assets/logos/. The data file stores only the file name (e.g."redwan.jpg"), not a path. Leave the value as an empty string to fall back to the placeholder.
publicationData.js is generated from the CV's BibTeX file rather than edited
by hand, so the website and the CV cannot drift apart. It carries a header
comment saying so. Each entry has a category of "Journal Article",
"Conference Paper", or "Under Review", which drives the badge and the
"Filter By Type" control, and an optional projectUrl that renders a
"Project Site" chip.
To add a paper, add the BibTeX entry to the CV's .bib file and regenerate,
or — for a quick one-off — append an object in the same shape and add its year
to the years array if that year is not already listed.
videoData.js holds two kinds of entry:
type: "youtube"with avideoId(the bare id, no query string).type: "file"with asrcpointing at an.mp4. The project websites host their own clips, so these are absolute URLs to those sites.
Group a video by setting group, and list the group in the videoGroups
array to control section order. Videos use preload="none", so nothing
downloads until a visitor presses play. If a file URL breaks, the card falls
back to a "Watch on the project site" link instead of showing a dead player.
A deep link 404s, but clicking through the site works.
The 404.html / index.html script pair is missing or out of sync. See
section 3. Also confirm 404.html actually landed in build/ after a build.
The site loads as a blank white page.
Open the browser console. If the JS and CSS files 404, homepage in
package.json does not match where the site is served from. See section 4.
The live site shows this README instead of the website. Settings → Pages → Source is pointing at the source branch. See The most common mistake.
Changes are not showing up. Check the Actions tab for a failed run. If the run succeeded, it is usually the browser or CDN cache — hard-reload with Ctrl/Cmd + Shift + R. GitHub's CDN can take a minute or two.
The build fails on a warning.
The workflow sets CI: false so warnings do not fail the build. If you build
locally in a CI-like shell, use CI=false npm run build.
Git shows every file as modified when nobody edited them.
Line endings. This repo has a .gitattributes that normalizes to LF. Run
git add --renormalize . once to settle it.
The custom domain resets after a deploy.
public/CNAME is missing. Setting the domain only in the Pages UI does not
survive a redeploy. See section 4.