I have classic web application rendered on server. I want to create admin panel as single page application in React. I want to server admin panel from https://smyapp.example.com/admin/. I try to use create-react-app
but it assumes that i serve SPA from root URL. How should I configure create-react-app
to serve app from "admin"
subdirectory? In documentation I found "homepage"
property but if I properly understand it requires complete url. I can't give complete url because my app is deployed in few environments.
In addition to your requirements, I am adding mine:
- It should be done by CD, through an env variable.
- If I need to rename the subdirectory, I should only have to change the env variable.
- It should work with react-router.
- It should work with scss (sass) and html.
- Everything should work normally in dev mode (npm start).
I also had to implement it in Angular2+ project not long ago, I found it harder to implement in React then in Angular2+ where you are good to go with ng build --base-href /<project_name>/
. source
Short version
- Before building, set
PUBLIC_URL
env variable to the value of your subdirectory, let use/subdir
for example. You can also put this variable into your.env.production
(in case you do not have that file you can check the doc) - In
public/index.html
add the base element bellow, this is for static files like images.
<base href="%PUBLIC_URL%/">
- Also in
public/index.html
, if you have customlink
element, make sure they are prefixed with%PUBLIC_URL%
(likemanifest.json
andfavicon.ico
href). - If you use
BrowserRouter
, you can addbasename
prop:
<BrowserRouter basename={process.env.PUBLIC_URL} />
- If you use
Router
instead, because you need access tohistory.push
method, to programmatically change page, do the following:
// history.tsx
import {createBrowserHistory} from 'history';
export default createBrowserHistory({ basename: process.env.PUBLIC_URL });
<!-- Where your router is, for me, App.tsx -->
<Router history={history}>
...
</Router>
- Use relative links inside your elements
<!-- "./assets/quotes.png" is also ok, but "/assets/quotes.png" is not -->
<img src="assets/quotes.png" alt="" />
- Move your
background-image
links from scss to jsx/tsx files (note that you may not need to do that if you use css files):
/*remove that*/
background-image: url('/assets/background-form.jpg');
<section style={{backgroundImage: `url('assets/background-form.jpg')`}}>
...
You should be done.
Additional informations
I preferred to use PUBLIC_URL
instead of homepage
in package.json
because I want to use env variable set on gitlab to set the subdir. Relevant resources about the subject:
- https://create-react-app.dev/docs/deployment/#building-for-relative-paths
- https://create-react-app.dev/docs/advanced-configuration/
- https://github.com/facebook/create-react-app/issues/998
PUBLIC_URL
override homepage
, and PUBLIC_URL
also take the domain name, if you provide one. If you set only homepage
, PUBLIC_URL
will be set to the value of homepage
.
If you do not want to use a base element in your index.html (I would not know why), you will need to append process.env.PUBLIC_URL
to every link yourself. Note that if you have react-router with a base element, but have not set basename
prop, you will get a warning.
Sass won't compile with an incorrect relative path. It also won't compile with correct relative path to your ../public/assets
folder, because of ModuleScopePlugin
restrictions, you can avoid the restriction by moving your image inside the src folder, I haven't tried that.
There seem to be no way of testing relative path in development mode (npm start). see comment
Finally, theses Stack Overflow links have related issues:
For create-react-app v2 and react-router v4, I used the following combo to serve a production (staging, uat, etc) app under "/app":
package.json:
"homepage": "/app"
Then in the app entry point:
<BrowserRouter basename={process.env.PUBLIC_URL}>
{/* other components */}
</BrowserRouter>
And everything "just works" across both local-dev and deployed environments. HTH!
<base href="/app/" />
in public/index.html as well to make the assets work - and assets use relative urls (like <link href="css.css" rel="stylesheet" />
). –
Allynallys You should add entry in package.json
for this.
Add a key "homepage": "your-subfolder/" in your package.json All static files will be loaded from "your-subfolder"
If there is no subfolder and you need to load from same folder you need to add the path as "./" or remove the entire line which has "homepage": "xxxxxxxxxx"
"homepage": "./"
From the official docs
By default, Create React App produces a build assuming your app is hosted at the server root. To override this, specify the homepage in your package.json, for example:
"homepage": "http://mywebsite.com/relativepath",
Note: If you are using react-router@^4
, you can route <Link>
s using the basename prop on any <Router>
.
From here and also check the official CRA docs
To get relative URLs you can build the app like this:
PUBLIC_URL="." npm run build
Maybe you could use react-router
and its relative basename
parameter which allows you to serve your app from a subdirectory.
basename
is the base URL for all locations. If your app is served from a sub-directory on your server, you’ll want to set this to the sub-directory. A properly formatted basename should have a leading slash, but no trailing slash.
For instance:
<BrowserRouter basename="/calendar"/>
So <Link to="/today"/>
will render <a href="/calendar/today">
See: https://reacttraining.com/react-router/web/api/BrowserRouter/basename-string
put in package.json something like this:
"homepage" : "http://localhost:3000/subfolder",
and work fine on any public or local server. Of course, subfolder must be your folder.
In our case, we did everything as described in Ambroise's answer, but got a blank page in production with no errors or warnings in the console. It turned out to be a problem with BrowserRouter - we got it working by setting BrowserRouter's basename like so:
<BrowserRouter basename="/our_subfolder/" />
I was facing the similar kind of issue. Need to serve react app from Godaddy hosting. let's say for example the context path should be /web ie:- http://example_123.com/web Following changes works fine for me with React 18.2.0 , react-router-dom: 6.11.0
set homepage in package.json to /web ie: homepage: "/web"
update basename in BrowserRouter to process.env.PUBLIC_URL
<BrowserRouter basename={process.env.PUBLIC_URL}>
{/*Your components*/}
</BrowserRouter>
build app and put your build inside /web folder in public_html ie: /public_html/web
on godaddy inside public_html created index.html and redirect that to /web
<html> <head>
<title>welcome to example_123</title>
<!--<meta http-equiv="refresh" content="0.2; URL=web/index.html"> -->
<meta name="keywords" content="automatic redirection">
</head>
<body> </body>
</html>
Bonus
If want to put your content inside /web and don;t want context path. follow below steps :-
- set homepage to /web in package.json
- put your build inside /web in public_html
- just move index.html from /web to publi_html ie.: put index.html generated by build inside public_html and all other files in /web
You can specify the public path in your webpack configuration along with use of react route basepath.
Link to Public Path: https://webpack.js.org/guides/public-path/
Note that public path will be both leading and trailing slashes / to be valid.
© 2022 - 2024 — McMap. All rights reserved.
router
basename
, I think this url is only for bundles, maybe you can link to a shared one likestatics.mywebsites.com
? – Eggcup