When running my application on the localhost I had no problem visiting urls that I manually typed in or refreshing the page but after using heroku to deploy the app, I can only visit pages by navigating through the home page. After reading some posts on here( React-router urls don't work when refreshing or writing manually ) , I decided to try to apply a "catch-all" solution. I have implemented the /* function in the 'server.js' file but the webpage loads the blank index.html page instead of the correct react component and can not figure out why. The code for the related pages are below:
Api.js
`
const request = require('request');
const express = require('express');
const app = express();
const router = express.Router();
var varViews = 0;
const {createServer} = require('http');
const path = require('path');
const PORT = process.env.PORT || 3000
const dev = app.get('env') !== 'production'
if(!dev){
console.log("Not Dev mode");
app.use(express.static(path.resolve(__dirname, 'build')));
app.use(express.static(__dirname + '/public'));
app.get("/", (req, res) => {
console.log('Home page');
res.send("This is the home page");
})
app.post("/usbstat", (req, res) => {
varViews++;
var views = {views: varViews};
res.json(views);
})
app.delete("/usbstat", (req, res) => {
varViews = 0;
var views = {views: 0};
res.json(views);
})
app.get("/usbstat", (req, res) => {
var numViews = {views: varViews};
return res.json(numViews);
})
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, '/public/index.html'), function(err) {
if (err) {
res.status(500).send(err)
}
})
})
}
app.listen(PORT, () => {
console.log('App is listening on'+PORT);
})
module.exports = app;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<!-- <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> -->
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Nova Cyber Security Test</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script src="../src/index.js"></script>
</body>
</html>
app.js
import React, { Component } from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import history from './history';
import Home from './pages/home/Home';
import USBDrive from './pages/usbdrive/USBDrive';
import phishingStats from './pages/phishingStats/phishingStats';
import Admin from './pages/admin/Admin';
class App extends Component {
render() {
return (
<Router history={history}>
<Switch>
<Route exact path='/' component={Home}/>
<Route exact path='/USBStats' component={USBDrive}/>
<Route exact path='/phishingStats' component={phishingStats}/>
<Route exact path='/admin' component={Admin}/>
</Switch>
</Router>
);
}
}
export default App;
Related
|project-name
| client
| public
| index.html
| server.js
↑ Project structure
My purpose is to display index.html(in public) in server.js.
[ server.js ]
const express = require('express')
const app = express()
const path = require('path')
app.listen(8080, function() {
console.log('listening on 8080')
})
app.use(express.static(path.join(__dirname, 'client/public')))
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'client/public/index.html'))
})
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'client/public/index.html'))
})
I wrote the code as above, but when I run node server.js to open the server and connect to localhost:8080, nothing happens.
It seems that the path is not wrong, but I wonder why the React project I made doesn't come out.
[ public > index.html ]
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/public/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="/public/logo192.png" />
<link rel="/manifest" href="/public/manifest.json" />
<title>Project Name</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
[ index.js in client ]
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom'; // 추가됨
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
reportWebVitals();
[ App.js in client ]
import React from 'react'
import { Route, Routes } from 'react-router-dom'
import Main from './pages/Main'
import Login from './pages/Login'
import Register from './pages/Register'
function App() {
return (
<div className='App'>
<div>
<Routes>
<Route path='/' element={<Main />} />
<Route path='/login' element={<Login />} />
<Route path='/register' element={<Register />} />
</Routes>
</div>
</div>
);
};
export default App;
Let me know if you need more code.
Here is the best way to render public HTML files.
Set the view engine like that.
app.set('view engine', 'html');
app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res) {
res.render('index');
});
The second option is no need to set the view engine.
app.get('/', (req, res) => {
res.sendFile(__dirname + '/public/about.html');
});
I am trying to implement server side rendering with my react app that uses HMR and need some clarification on my issues. There seems to be miscommunication between the server and the frontend but I'm not entirely sure why that is.
When I start my server, the browser loads the client side index.html and I am able to edit my site with HMR working just fine. However, when I navigate to another page besides the homepage, and I refresh the browser, the server then renders the html markup in my handleRender function. The sites html markup appears, however there is no css and HMR doesn't work.
To summarize, on first load, it renders the app client side, but upon refresh, it renders the app server side. I'm thinking it might have to do with the way I am using router.use(express.static(clientDir)); but I'm not sure. Any clarification would be helpful!
Client side Index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Base Template</title>
<link rel="stylesheet" type="text/css" href="/static/bundle.css">
</head>
<body id="body">
<div class="root"></div>
<script src="/static/bundle.js"></script>
</body>
</html>
React App index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import { BrowserRouter } from 'react-router-dom'
import App from './App'
const root = document.querySelector('.root');
// Wraps our App in AppContainer
const render = (Component) => {
ReactDOM.render(
<BrowserRouter>
<AppContainer>
<Component/>
</AppContainer>
</BrowserRouter>,
root
);
};
// Renders our application
render(App);
// Checks if there have been changes and accepts them.
// This activates HMR and refreshes modules in the browser
if (module.hot) {
module.hot.accept();
}
Express Server
import express from 'express'
import React from 'react'
import { renderToString } from 'react-dom/server'
import { StaticRouter } from 'react-router'
import { resolve } from 'path'
import routes from './routes'
// Imports our React App
import App from '../client/scripts/App'
// Create Express App
const app = express();
// Set the port for our server
app.set('port', process.env.PORT || 3000)
// Use routes to check if the environment is prod || dev
app.use(routes)
// Use handleRender for SSR
app.use(handleRender)
function handleRender(req, res) {
const context = {};
const html = renderToString(
<StaticRouter location={req.url} context={context} >
<App/>
</StaticRouter>
)
if (context.url) {
res.writeHead(301, {
Location: context.url
})
res.end()
} else {
res.write(`
<!doctype html>
<head>
<title>Redux Universal Example</title>
<link rel="stylesheet" type="text/css" href="/static/bundle.css">
</head>
<body>
<div id="app">${html}</div>
<script type="javascript" src="/static/bundle.js"></script>
</body>
`)
res.end()
}
}
export default app
Routes for server
import express, { Router } from 'express'
import { resolve } from 'path'
import { isDev } from '../utils'
const router = Router();
const clientDir = resolve(`${__dirname}/../../client`);
if (isDev()) {
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
const webpackConfig = require('../../../webpack.config')
const webpackHotMiddleware = require('webpack-hot-middleware')
const compiler = webpack(webpackConfig)
// This compiles our app using webpack
router.use(webpackDevMiddleware(compiler, {
publicPath: webpackConfig.output.publicPath,
noInfo: true
}))
// This connects our app to HMR using the middleware
router.use(webpackHotMiddleware(compiler))
}
router.use(express.static(clientDir));
export default router
The issue is when I do npm run start-dev to run client side code, it works perfectly. But whenever I run npm run start to start up the server side. The Client side doesn't dispatch the content back to UI. Need help.
Server.js
'use strict';
import path from 'path';
import { Server } from 'http';
import Express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import routes from './routes';
import NotFoundPage from './components/NotFoundPage';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
const content = require('./Action/reducer');
// initialize the server and configure support for ejs templates
const app = new Express();
const server = new Server(app);
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// define the folder that will be used for static assets
app.use(Express.static(path.join(__dirname, 'static')));
// universal routing and rendering
app.get('*', (req, res) => {
match(
{ routes, location: req.url },
(err, redirectLocation, renderProps) => {
const store = createStore(content);
// in case of error display the error message
if (err) {
return res.status(500).send(err.message);
}
// in case of redirect propagate the redirect to the browser
if (redirectLocation) {
return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
}
// generate the React markup for the current route
let markup;
if (renderProps) {
// if the current route matched we have renderProps
markup = renderToString(
<Provider store={store}>
<RouterContext {...renderProps}/>
</Provider>
);
} else {
// otherwise we can render a 404 page
markup = renderToString(<NotFoundPage/>);
res.status(404);
}
res.send(renderFullPage(markup))
// render the index template with the embedded React markup
// return res.render('index', { markup });
}
);
});
function renderFullPage(html) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/img/favicon.ico">
<link rel="apple-touch-icon" sizes="76x76" href="assets/img/apple-icon.png">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>Chris</title>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
<meta name="viewport" content="width=device-width" />
<link href="bootstrap3/css/bootstrap.css" rel="stylesheet" />
<link href="assets/css/gsdk.css" rel="stylesheet" />
<link href="assets/css/demo.css" rel="stylesheet" />
<!-- Font Awesome -->
<link href="bootstrap3/css/font-awesome.css" rel="stylesheet"/>
<link href='https://fonts.googleapis.com/css?family=Grand+Hotel' rel='stylesheet' type='text/css'/>
</head>
<body>
<div id="main">${html}</div>
<script src="../static/js/bundle.js"></script>
<script src="jquery/jquery-1.10.2.js" type="text/javascript"></script>
<script src="assets/js/jquery-ui-1.10.4.custom.min.js" type="text/javascript"></script>
<script src="bootstrap3/js/bootstrap.js" type="text/javascript"></script>
<script src="assets/js/gsdk-checkbox.js"></script>
<script src="assets/js/gsdk-radio.js"></script>
<script src="assets/js/gsdk-bootstrapswitch.js"></script>
<script src="assets/js/get-shit-done.js"></script>
<script src="assets/js/custom.js"></script>
</body>
</html>
`
}
// start the server
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || 'production';
server.listen(port, err => {
if (err) {
return console.error(err);
}
console.info(`Server running on http://localhost:${port} [${env}]`);
});
Client:
'use strict';
import React from 'react';
import { render } from 'react-dom'
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import Layout from './components/Layout';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
const reducer = require('./Action/reducer');
const store = createStore(reducer);
// import { createStore } from 'redux';
// const content = require('./Action/reducer');
// const store = createStore(content);
// import Layout from './components/Layout';
render(
<Provider store={store}>
<Router history={browserHistory} routes={routes} onUpdate={() => window.scrollTo(0, 0)}/>
</Provider>,
document.getElementById('main')
)
Layout.js
'use strict';
import React from 'react';
import { Link } from 'react-router';
// const actions = require('../Action/actions');
import * as actions from '../Action/actions';
import {connect} from 'react-redux';
class Layout extends React.Component {
handleSwitchLang(targetLang) {
this.props.switchLanguage(targetLang);
}
render() {
let switchLanguage = this.props.switchLanguage;
let content = this.props.content;
return (
some UI code
);
}
}
function mapStateToProps(state) {
return { content: state.content }
}
export default connect(mapStateToProps, actions)(Layout);
reducer.js
const action_types = require('./action_types');
import json from '../../lang.json';
const initialState = {
content: json.en // Loads default language content (en) as an initial state
};
const reducer = function (state = initialState, action) {
switch (action.type) {
case action_types.SWITCH_LANGUAGE:
return {
content: json[action.language]
};
default:
return state;
}
};
module.exports = reducer;
You can try out my code if you want. Here is my github project.
https://github.com/luznlun/samwebsite
clone the project. do npm install, npm runs start-dev to run client side(Working), npm run start(problem with dispatch).
Really appreciate the help
I have my app code here: https://github.com/WebTerminator/aldemar/commits/master
and I am trying to get my react app to work on the server side as well, at this stage it works partially.
The problem I have is: (sam problem happens on localhost)
if I navigate within the browser it all works fine, the moment I refresh this URL https://aldemar-productions.herokuapp.com/projects/margam2 I get a console error like:
bundle.js:1 Uncaught SyntaxError: Unexpected token <
If I refresh others URLs like "https://aldemar-productions.herokuapp.com/projects" or "https://aldemar-productions.herokuapp.com/about" they work fine.
server.js
import express from 'express';
import path from 'path';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import routes from './src/client/app/config/routes.jsx';
let port = process.env.PORT || 8080;
let app = express();
app.use(express.static('src/client/'));
// app.get('/', (req, res) => {
// res.sendFile(path.resolve(__dirname + '/src/client/index.html'))
// });
app.get('*', (req, res) => {
match(
{ routes, location: req.url },
(err, redirectLocation, renderProps) => {
// in case of error display the error message
if (err) {
return res.status(500).send(err.message);
}
// in case of redirect propagate the redirect to the browser
if (redirectLocation) {
return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
}
// generate the React markup for the current route
let markup;
if (renderProps) {
// if the current route matched we have renderProps
markup = renderToString(<RouterContext {...renderProps}/>);
}
// else {
// // otherwise we can render a 404 page
// markup = renderToString(<NotFoundPage/>);
// res.status(404);
// }
// render the index template with the embedded React markup
return res.sendFile('index.html', {root : __dirname + '/src/client/'});
}
);
});
app.listen(port);
console.log('server started');
routes.jsx
import React from 'react';
import { Route, Router, browserHistory } from 'react-router';
import ReactDOM from 'react-dom';
import Wrapper from './../components/wrapper.jsx';
import Home from './../components/home.jsx';
import Projects from './../components/projects.jsx';
import SingleProject from './../components/projectContent/singleProject.jsx';
import About from './../components/aboutUs.jsx'
if(typeof window !== 'undefined') {
console.log('here baby');
ReactDOM.render((
<Router history={browserHistory} >
<Route component={Wrapper} >
<Route path="/" component={Home} />
<Route path="projects" component={Projects} />
<Route path="projects/:id" component={SingleProject} />
<Route path="about" component={About} />
</Route>
</Router>
), document.getElementById('app'));
}
singleProject.jsx (where I get the ID parameter from url to load the specific data)
import React from 'react';
import Video from './../video.jsx';
import Overview from './overview.jsx';
import Photography from './photography.jsx';
import Details from './details.jsx';
import Cast from './cast.jsx';
import porgectsCollection from './../../data/projectInfo.js';
import { StickyContainer, Sticky } from 'react-sticky';
class Nav extends React.Component {
constructor(props) {
super(props);
this.state = {
mobileMenu: false
};
}
showMobileMenu () {
this.setState({ mobileMenu: !this.state.mobileMenu });
}
render () {
let links = this.props.project.links.map(function(el, i){
return <li key={i}>{el}</li>;
});
const open = this.state.mobileMenu ? ' open' : '';
return (
<Sticky stickyClassName="sticky-nav" topOffset={-100}>
<span onClick={this.showMobileMenu.bind(this)} className="mobile-trigger">X</span>
<nav className={"secondary-nav" + open}>
<ul>
{links}
</ul>
</nav>
</Sticky>
);
}
}
class SingleProject extends React.Component {
getProjectDataFromUrl() {
return **porgectsCollection.filter(el => el.title === this.props.params.id)**;
}
render () {
let data = this.getProjectDataFromUrl(),
project = data[0];
return (
<section className="project-page">
<StickyContainer>
<Video project={project} />
<Nav project={project} />
<Overview project={project} />
<Photography project={project} />
<Details project={project} />
<Cast project={project} />
</StickyContainer>
</section>
);
}
}
export default SingleProject;
When I hit a url like this "https://aldemar-productions.herokuapp.com/projects/margam2" I collect "margam2" as per my routes:
<Route path="projects/:id" component={SingleProject} />
and I load specific data based on that parameter. I believe the problem is around here with doing server side rendering.
UPDATE-1
adding this in the head of my index.html allows to get the content displayed when I refresh the page however the CSS is missing:
<base href="/" />
the css is fully accessible at http://localhost:8080/css/style.css, however when I refresh "http://localhost:8080/projects/margam2" the content is displayed but not the css.
Ok after 2 days of research, this has helped me:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
**<base href="/" />**
<link rel="stylesheet" href="./css/foundation.min.css">
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
<link rel="stylesheet" href="./css/font-awesome.min.css">
<link rel="stylesheet" href="./css/style.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css" />
</head>
<body>
<div id="app"></div>
<script src="./public/bundle.js" type="text/javascript"></script>
</body>
</html>
however I also found out this in my console:
browser.js:49 Warning: Automatically setting basename using is deprecated and will be removed in the next major release. The semantics of are subtly different from basename. Please pass the basename explicitly in the options to createHistory
so need to find a solution for this.
update-1
even better solution I think. I have changed the assets path form relative to absolute:
see the "/" in front of every local asset
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="/css/foundation.min.css">
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
<link rel="stylesheet" href="/css/font-awesome.min.css">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css" />
</head>
<body>
<div id="app"></div>
<script src="/public/bundle.js" type="text/javascript"></script>
</body>
</html>
On your project's page, you are using an inline close div, like this:
<div id="app" />
In all your other pages, you are using a empty div
<div id="app" > </div>
And that's the right way to do it.
React-Router do not re-render your page, it only "re-add" your react code to your id="app".
That's why when you open clicking it works and when you refresh it broke.
I have a React app that also uses Redux and ReactRouter.
My problem is the following:
When I launch the app and go to the root url, I can normally navigate inside the app, and the routes in the navigation bar will be changing as I navigate.
However, if I type in the navigation bar any url other than the root, I get a weird error:
I don't really get how one can get such an error.
If I go to localhost:1337/ and then click on the item with the link /cars/1, everything will be fine and the component will successfully get rendered. If I type localhost:1337/cars/1 right away (or any other existing route) I get this error.
Here's how I initialize the react-router and define my routes:
index.js:
require('./style/style.css');
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, browserHistory } from 'react-router';
import reduxPromise from 'redux-promise';
import routes from './routes';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(
reduxPromise
)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<Router history={browserHistory} routes={routes} />
</Provider>
, document.querySelector('.container.app')
);
routes.js:
import React from 'react';
import { Route, IndexRoute, Redirect } from 'react-router';
import App from './components/app';
import CarsIndex from './containers/cars-index';
import CarNew from './containers/car-new';
import CarShow from './containers/car-show';
import CarEdit from './containers/car-edit';
import SignIn from './containers/signin';
import auth from './auth/auth';
function requireAuth(nextState, replace) {
if (!auth.loggedIn()) {
replace({
pathname: '/authenticate',
state: { nextPathname: nextState.location.pathname }
});
}
}
function filterLoggedIn(nextState, replace) {
if (auth.loggedIn()) {
replace({
pathname: '/',
state: { nextPathname: nextState.location.pathname }
});
}
}
export default (
<Route path='/' component={App}>
<IndexRoute component={CarsIndex} onEnter={requireAuth} />
<Route path='cars/new' component={CarNew} onEnter={requireAuth} />
<Route path='cars/:id' component={CarShow} onEnter={requireAuth} />
<Route path='cars/edit/:id' component={CarEdit} onEnter={requireAuth} />
<Route path='authenticate' component={SignIn} onEnter={filterLoggedIn} />
<Redirect from='*' to='/' />
</Route>
);
My server is a small express.js app and it redirects any requests other than /api/* to the index.html page.
Here's a part from my server.js:
const path = require('path');
const port = process.env.PORT || 1337;
const app = express();
const pathToStatic = path.join(__dirname, 'static');
express.static(path.join(__dirname, 'static'));
app.use(express.static(pathToStatic));
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.get('/api/cars', (req, res) => {
return Car.find((err, cars) => {
if (!err) {
return res.send(cars);
} else {
console.log(err);
res.statusCode = 500;
return res.send({ error: 'Server error' });
}
});
});
app.get('*', (req, res) => {
res.sendFile(path.resolve(pathToStatic, 'index.html'));
});
app.listen(port, () => {
console.log(`Express server is listening on port ${port}`);
});
Have you encountered such an issue? Could you help me to find out the soultion to this problem?
The screenshot shows that bundle.js (when requested) is returning index.html.
This is because your express routes handle route api/cars and then default everything else to index.html.
Of course all your resources that appear on index.html must also be sent the browser. This includes <script src="bundle.js"></script> which the browser will request once it gets index.html the first time.
So, you must have some way to allow express to handle requests for the resources that index.html needs.
A popular solution to this is to mount an assets directory and place that above the default route. Something like:
// api routes
app.use(express.static('assets'));
// default route
Then make sure your bundle.js is inside the assets directory. And then that the script tag looks like <script src="/assets/bundle.js"></script>.