My Heroku app is using React with React Router. I use Switch to navigate through different components, so the URL changes as well (e.g. /room/4141). However, if I reload the page, it doesn't act like if it was a React app, but instead it searches for the mentioned .html file.
I used this Buildpack: https://github.com/mars/create-react-app-buildpack.git but it seems to do nothing in regards with pages being rewritten to index.html.
Is there a way to prevent this behaviour and rewrite all URLs to index.html?
**EDIT:
I'm not familiar enough with express, but here's how the index.html is served.
const express = require("../../node_modules/express");
const app = express();
const server = require("http").Server(app);
const io = module.exports.io = require('../../node_modules/socket.io/lib')(server)
const path = require("path")
app.use(express.static(path.join(__dirname, '../../build')));
if(process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, '../../build')));
console.log("DEBUG HERE", __dirname, path.join(__dirname+'../../build'));
//
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname+'../../build/index.html'));
})
}
//build mode
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname+'../../public/index.html'));
})
That buildpack can be configured via a JSON file:
You can configure different options for your static application by writing a static.json in the root folder of your application.
One of the sample routing configurations looks like it does exactly what you want:
When serving a single page app, it's useful to support wildcard URLs that serves the index.html file, while also continuing to serve JS and CSS files correctly. Route ordering allows you to do both:
{
"routes": {
"/assets/*": "/assets/",
"/**": "index.html"
}
}
Related
I'm an express noob here and building a React App with server using express and client using create-react-app.
What I want to do
I want to update the title and meta tag in the index.html.
So browser requests url -> Server gets request and adds the title and tag to the index.html -> return it to the browser.
Listed my code here
...
app.use(bodyParser.json())
app.use(aMiddleware)
app.use("/api/foo", bar)
app.use(express.static('client/build'));
if (process.env.NODE_ENV === 'production') {
const path = require('path');
app.get('/*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../client', 'build', 'index.html'))
})
}
Questions
Code is functioning, but I don't know how to replace the title/tag in the index.html
How do I update/replace index for environment that is not prod?
Fo prod environment, I use path.resolve(__dirname, '../client', 'build', 'index.html'), then where is index.html for dev environment? I see there is an index.html in public folder, is it the one that got rendered for dev environment?
I tried to add this code before app.use(express.static(...))
app.get('/', function(req, res) => {
// maybe replace string in the index.html (though I don't know where is it
// then res.send(...)?
})
but this never got triggered. Why?
Stuck on this for a while, tried many things, any help would be great.
You can use react-helmet for this... Or switch to Nextjs which is server side.
https://www.npmjs.com/package/react-helmet
So I would like to make npm run build to produce local runnable files. Basically, I want it to build index.js with importing assets like ./static/js/chunk.js instead of /static/js/chunk.js. The client I am running this on does not have internet access so I want to serve the app from the file system.
Basically, I want to open index.html in the build folder with chrome and the app just work. Any ideas on how to do this?
Using simple node js and serve it as static is the best way because react router dom need url in the app.
const express = require("express");
const path = require("path");
const app = express();
app.use("/static", express.static());
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "frontend", "build", "index.html"));
});
app.listen(6000, () => console.log("Listening to port 6000"));
Next to node file index.js, or whatever you name it, make the folder called frontend. Inside it put your build from react so the node can serve index.html in port 6000
Context: I'm opening up my React dev environment to external hits using a paid version of the tool ngrok -- I am running WebPack 4.0 with a devServer.
I go to my app's main page:
https://my-example-domain.ngrok.io
Loads fine.
I click one of the links on my app's main page, The router works well and I see the content for:
https://my-example-domain.ngrok.io/my-sub-page
However, if I refresh at this point, ngrok gives out a 404:
Cannot GET /my-sub-page
The reason I suspect is that in a SPA, the internal app's URLs are processed by the front end in React-Router and externally, there is no resource called /my-sub-page
Is there a way to force this to work?
The problem is that the local server doesn't know about your spa routes(react-router). To fix this, you have to create a simple server that will serve your static build and have a fallback route that will enable spa-routing.
Example in express.js:
import express from 'express';
import path from 'path';
// ... other imports
const app = express();
app.use(express.static('build'));
/*
... some route handlers
*/
// * Unknown endpoint
app.use("/*", (_req, res) => {
res.sendFile(path.join(__dirname, "../build/index.html"), (err) => {
if (err) {
res.status(500).send(err);
}
});
});
app.listen(process.env.PORT || 3000 , ()=>{
console.log('server running')
})
This resolved it -- in webpack.config.js
devServer: {
...
...
historyApiFallback: true, // <- inserting this resolved the issue.
}
I have a react application built using CRA with the default configurations. I wanted to make SSR, so I followed an article something similar to this https://www.vairix.com/tech-blog/server-side-rendering-ssr-of-create-react-app-cra-app-in-2020
Next, I wanted to inline the JS and CSS so that I could hit the URL and copy the resultant HTML page and then use it where ever I want.
For that, I used react-app-rewired plugin which works now I can see the HTML with inline CSS and JS.
The issue is when I copy the generated HTML and save it as .html, and when I open the page, it returns 404 error.
I am trying to copy the HTML that is produced and then use them as individual components in a completely different application.
Am I missing something?
config-overrides.js for react app rewired
const HtmlWebpackPlugin = require("html-webpack-plugin");
const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin");
const HTMLInlineCSSWebpackPlugin = require("html-inline-css-webpack-plugin").default;
module.exports = {
webpack: function(config, env) {
if (env === "production") {
config.plugins.push(new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/.*/]));
config.plugins.push(new HTMLInlineCSSWebpackPlugin());
}
return config;
}
};
server.js for SSR
import express from "express";
import path from "path";
import renderer from "./react-renderer";
const app = express();
const PORT = 8000;
const routes = ["/custom1/:id","/custom2/:id","/custom3/:id"];
app.get("/*", renderer(routes));
app.use(express.static(path.resolve(__dirname, "../build")));
app.use(express.static(path.resolve(__dirname, "../public")));
app.listen(PORT, () => {
console.log(`App running in the port ${PORT}`);
});
404 is the HTTP code for not found: I don't think the problem is in opening the page but in finding it.
It seems the first route you added in your server is catching all the requests:
app.get("/*", renderer(routes));
you could try moving it as last route
app.use(express.static(path.resolve(__dirname, "../build")));
app.use(express.static(path.resolve(__dirname, "../public")));
app.get("/*", renderer(routes));
More than this you didn't said where you copied the resulting page, I hope in ../build or in ../public directories, otherwise I'm not surprised you can't GET it.
I'm having trouble with reactjs in production, in my development machine i can easily refresh/reload without redirecting to 404 (not found page) but during production every time i reload or refresh in http://example.com/subdirectory/dashboard it will always go to 404 page of http://example.com but when I'm in development machine http://localhost:3000/subdirectory/dashboard when I refresh/reload the page it will reload as expected.
Note: In production I uploaded my static data into subdirectory so i use basename in my route.
If your serving your application on a standard webserver you can add a .htaccess using the following which enables rewrite rules:
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]
It's likely that the 404 issue you're facing will be solved by simply just serving the above alongside the root of the project.
Alternately you could use the following as well which would serve it as a node server:
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'build')));
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(9000);
The above can be used in conjunction with a .htaccess.
The create react app guys also promote serve which you can find here and the official react deployment documentation here
1: https://github.com/zeit/serve
2: https://create-react-app.dev/docs/deployment#docsNav
In your http server you have to rewrite all urls to your index.html file. If you are using nginx you can follow this: React-router and nginx
You might have to create a proxy server for your React.js application in production. You can do this in different ways but to do it with Express Node.js, you would have to create a file that looks like this:
const express = require('express');
const path = require('path');
const port = 7700;
const app = express();
app.use(express.static(path.join(__dirname, '../public')));
app.all('*', (req, res) => {
res.sendFile(path.join(__dirname, '../public/index.html'));
});
app.listen(port, () => {
console.log(`Listening on ${port}`);
});
The snippet above assumes you have built your React files and your index.html is in a public directory. This proxies all requests from the express server to your React routes with the * wildcard.
With that done, all that's left is to run this express app on your server and it handles your history API routes just like regular server routes.
add basename inside your src/index.js like this
(you can also add <base href="/subdirectory" target="_blank"> in your index.html file's head tag)
<Router
basename={'/subdirectory'}
>
<App />
</Router>
In your public folder make a file _redirects and write
/* /index.html 200 in it