Search code examples
reactjsreact-routerprerenderreact-snap

why react-snap only pre-render main page in SPA application?


I have a simple project SPA application base on react and react-router-4.I have multiple routes in my project like:

  • www.mySite/build/#/
  • www.mySite/build/#/page1/page12
  • www.mySite/build/#/page2

When I pre-render my project with react-snap and open routes in browser, only main route (www.mySite/build/#/) pre-rendered so google robot can not crawl other routes and pages.

my package.json config:

{
  "name": "react-boilerplate",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack-dev-server --mode development --open --hot",
    "build": "webpack --mode production",
    "postbuild": "react-snap"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.1.2",
    "@babel/preset-env": "^7.1.0",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.4",
    "css-loader": "^1.0.0",
    "html-webpack-plugin": "^3.2.0",
    "react-snap": "^1.23.0",
    "style-loader": "^0.23.0",
    "webpack": "^4.20.2",
    "webpack-cli": "^3.1.1",
    "webpack-dev-server": "^3.1.14"
  },
  "dependencies": {
    "react": "^16.5.2",
    "react-dom": "^16.5.2",
    "react-redux": "^7.1.0",
    "react-router": "^5.0.1",
    "react-router-dom": "^5.0.1",
    "redux": "^4.0.4"
  }
}

my webpack config:


const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.join(__dirname, "/build"),
    filename: "index-bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ["babel-loader"]
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html"
    })
  ]
};

index.js:

import React from "react";
import {Route, HashRouter as Router, Switch} from 'react-router-dom';
import App from "./components/App.js";
import Page1 from "./components/Page1";
import Page12 from "./components/Page12";
import Page2 from "./components/Page2";
import { hydrate, render } from "react-dom";

const route = (
    <Router>
        <Switch>
            <Route exact path="/" component={App}/>
            <Route exact path="/page1" component={Page1}/>
            <Route exact path="/page1/page12" component={Page12}/>
            <Route exact path="/page2" component={Page2}/>
        </Switch>
    </Router>
);



const rootElement = document.getElementById("root");

if (rootElement.hasChildNodes()) {
    hydrate(route, rootElement);
} else {
    render(route, rootElement);
}

App.js:

import React, { Component } from "react";

import '../styles/App.css';

import {NavLink} from 'react-router-dom';

class App extends Component {
    render() {
        return (
            <div>
                <h1>My React App!</h1>
<br/>
                <NavLink  exact to="/page1">صفحه 1</NavLink>
                <br/>
                <NavLink  exact to="/page1/page12">صفحه 12</NavLink>
                <br/>
                <NavLink   exact to="/page2">صفحه 2</NavLink>
                <br/>

            </div>
        );
    }
}


export default (App);

codes in page1:

import React, { Component } from "react";

class Page1 extends Component {
    render() {
        return (
            <div>
                <h1>we are in page1</h1>
            </div>
        );
    }
}


export default(Page1);

Other page are like page1.


Solution

  • Google bot will ignore url content after # in your URL, causing

    www.mySite/build/#/page1/page12 => www.mySite/build/#/

    To make your pre-render work, you might want to take away the # in your url and make it like www.mySite/build/page1/page12

    See this: How to remove the hash from the url in react-router