Search code examples
node.jsreactjsazurereact-router-domproduction-environment

React Router doesnot match path = "/" in production


Load this site: https://portfolio-website.azurewebsites.net/

Expected behaviour: Loads the site content properly.

Actual behaviour: You will see a blank page with no contents.

But try adding, /{anything} at the end of the url e.g. https://portfolio-website.azurewebsites.net/x, the website loads perfectly. React router is not catching the path "/" which is really confusing but when you go to about link and then click on home the site loads the homepage.

I am currently using azure, but the same results in heroku too. Help!

You can view the source code here and make contributions here: https://github.com/CrestNiraj12/portfolio-website

App.js

const App = ({ page, overflowHidden, isLandscape, dialogShow, loading }) => {
  useEffect(() => {
    if (overflowHidden) document.body.style.overflow = "hidden";
    else document.body.style.overflow = "visible";

    isLandscape(window.innerWidth > window.innerHeight);
  }, [page, isLandscape, overflowHidden]);

  const routes = [
    { path: "/", Component: Home, isExact: true },
    { path: "/about", Component: About, isExact: false },
    { path: "/update/posts/:postId", Component: EditPost, isExact: false },
    { path: "/posts/:postPath", Component: Post, isExact: false },
    { path: "/auth/login", Component: Login, isExact: false },
    { path: "/auth/register", Component: Signup, isExact: false },
    {
      path: "/auth/password/recover",
      Component: ConfirmRecoverPassword,
      isExact: false,
    },
    { path: "/user/dashboard", Component: Dashboard, isExact: false },
    { path: "/posts", Component: Posts, isExact: false },
    { path: "/users", Component: Users, isExact: false },
    { path: "/user/addpost", Component: AddPost, isExact: false },
    { path: "/user/confirm/:token", Component: ConfirmMail, isExact: false },
    {
      path: "/password/recover/token/:token",
      Component: ResetPassword,
      isExact: false,
    },
    { path: "/*", Component: Home, isExact: false },
  ];

  return (
    <>
      <Flash />
      {dialogShow && <Dialog />}

      {loading && <Preloader />}
      <Router history={createBrowserHistory()}>
        {[HOME, ABOUT, ALL_POSTS, POST].includes(page) && <Navbar />}
        <Switch>
          {routes.map(({ path, Component, isExact }) => (
            <Route
              key={path}
              path={path}
              exact={isExact}
              component={Component}
            />
          ))}
        </Switch>
      </Router>
    </>
  );
};

Server.js

const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
const passport = require("./backend/config/passport");
const session = require("express-session");
const MongoStore = require("connect-mongo")(session);
const helmet = require("helmet");
const path = require("path");

require("dotenv").config({
  path: `.env.${process.env.NODE_ENV.trim() || "development"}`,
});

const app = express();
const port = process.env.PORT || 5000;

const postsRouter = require("./backend/routes/posts");
const usersRouter = require("./backend/routes/users");
const authenticationRouter = require("./backend/routes/authentication");

app.use(cors());
app.use(helmet());
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.static(__dirname + "/client/public"));

const url = process.env.CONNECTION_URL;

mongoose
  .connect(url, {
    auth: {
      user: process.env.DB_USER,
      password: process.env.DB_PASS,
    },
    useNewUrlParser: true,
    useCreateIndex: true,
    useUnifiedTopology: true,
    useFindAndModify: false,
  })
  .catch((err) => console.log(err));

const connection = mongoose.connection;

const sessionStore = {
  name: "authSession",
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: true,
  cookie: { httpOnly: true },
  store: new MongoStore({
    mongooseConnection: connection,
    secret: process.env.STORE_SECRET,
    ttl: 24 * 60 * 60 * 1000,
    autoRemove: "interval",
    autoRemoveInterval: 10,
  }),
};

if (process.env.NODE_ENV === "production") {
  const hour = 24 * 60 * 60 * 1000;
  app.set("trust proxy", 1);
  sessionStore.cookie.secure = true;
  sessionStore.cookie.maxAge = hour;
  sessionStore.cookie.expires = new Date(Date.now() + hour);
}

app.use(session(sessionStore));
app.use(passport.initialize());
app.use(passport.session());

app.use("/posts", postsRouter);
app.use("/user", usersRouter);
app.use("/auth", authenticationRouter);

connection
  .once("open", () => {
    console.log("Established database connection!");
  })
  .catch((err) => console.log(err));

if (process.env.NODE_ENV === "production") {
  app.use(express.static("client/build"));
  app.get("/*", (req, res) => {
    res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
  });
}

app.listen(port, () => console.log("Server running on port: " + port));

Server-side package.json

{
  "name": "portfolio-website",
  "version": "1.0.0",
  "description": "My Personal website",
  "main": "server.js",
  "repository": {
    "type": "git",
    "url": "https://github.com/CrestNiraj12/portfolio-website.git"
  },
  "scripts": {
    "start": "NODE_ENV=production node server",
    "azure": "NPM_CONFIG_PRODUCTION=false npm run imagemin && npm install --prefix client && npm run build --prefix client",
    "imagemin": "imagemin client/src/images/*.{jpg,png,gif} --out-dir=client/src/images",
    "dev": "concurrently \"nodemon server\" \"cd client && npm start\" \"cd client && npm run watch-sass\"",
    "serve": "serve -s client/build"
  },
  "author": "Niraj Shrestha",
  "license": "ISC",
  "engines": {
    "node": ">=12.0.0",
    "npm": ">=6.0.0"
  },
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "connect-mongo": "^3.2.0",
    "cors": "^2.8.5",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "express-session": "^1.17.1",
    "express-validator": "^6.6.0",
    "helmet": "^3.23.3",
    "imagemin-cli": "^6.0.0",
    "lodash": "^4.17.19",
    "mongoose": "^5.9.22",
    "multer": "^1.4.2",
    "nodemailer": "^6.4.11",
    "nodemailer-smtp-transport": "^2.7.4",
    "passport": "^0.4.1",
    "passport-local": "^1.0.0"
  },
  "devDependencies": {
    "concurrently": "^5.3.0",
    "nodemon": "^2.0.4",
    "serve": "^11.3.2"
  }
}

Solution

  • You're serving react static files with express, before setting the reactjs routes:

    app.use(express.static(__dirname + "/client/public"));
    ...
    app.get("/*", (req, res) => {
      res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
    });
    

    But the problem is : there is a static index.html file in the react public folder. that's why you're seeing a blank page on root. it's the static index.html file in react public folder, and it doesn't contains the react script tag. react-scripts uses it and appends the react <script src="..."> tag to it. so you can't remove it. instead set a route for root that points to react, before serving the static files :

    app.get("/", (req, res) => { // the root
      res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
    }
    ...
    app.use(express.static(__dirname + "/client/public")); // serving static files
    ...
    app.get("/*", (req, res) => { // all remaining addresses
      res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
    }