Skip to content

Commit c696eef

Browse files
committed
Add BASE_HREF and clientEnv to readme and templates.
1 parent 3860510 commit c696eef

File tree

7 files changed

+62
-35
lines changed

7 files changed

+62
-35
lines changed

README.md

+18-13
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,28 @@ During development, the request handler will be integrated into the local webpac
4747

4848
If `src/index.ssr.js` exports a function called `devServerHandler`, it will be invoked and its return value will be used as a request handler during development instead. This gives your entry point the possibility to make environment-specific adjustments. For example, all assets are compiled in-memory during development and should not be served from the real file system.
4949

50-
### Relative Urls
51-
`create-react-app` provides a variable called `PUBLIC_URL`, which is accessible via `process.env.PUBLIC_URL` in JavaScript or by using the placeholder `%PUBLIC_URL%` in Html.
50+
### Relative Urls, PUBLIC_URL and BASE_HREF
51+
`create-react-app` provides a variable called `PUBLIC_URL`, which is accessible via `process.env.PUBLIC_URL` in JavaScript or by using the placeholder `%PUBLIC_URL%` in HTML to reference assets. This variable is determined at **compile time** and must be specified before compilation, which makes your compiled web app dependant on the specified location.
5252

53-
For non-ssr projects, the public url has to be known at **compile time**. It is defined via Webpack's `publicPath` property and baked into the client-side js file with the `DefinePlugin` which hardcodes `process.env` into the compiled script.
53+
For projects with server-side rendering however, there is another, possibly better way to deal with this. Instead of hardcoding the public url, HTML assets can be referenced with a relative url and the server-side renderer can read a specified base url at **runtime** and render it into a base tag. `react-scripts-with-ssr` supports using `PUBLIC_URL` but recommends the described approach using the `BASE_HREF` environment variable:
54+
* Define a `BASE_HREF` environment variable in your server runtime environment containing your base url (e.g. `https://example.com/folder`).
55+
* Add `<base href="%BASE_HREF%/" />` to your index.html template. Notice the trailing slash.
56+
* Replace `%BASE_HREF%` with the value of `process.env.BASE_HREF` in your server-side request handler.
57+
* Make all asset references in HTML relative. Do not use `%PUBLIC_URL%` in URLs or server-relative URLs (e.g. `/some/url`).
58+
* Make the build scripts emit relative paths by providing `PUBLIC_URL` as environment variable with the value `.` during compile time or setting the `homepage` field in `package.json` to `.`.
59+
* If you need access to BASE_HREF in JavaScript, read it from `document.getElementsByTagName("base")[0].href;`.
5460

55-
For ssr projects however, it is possible to configure the PUBLIC_URL as environment variable on the server-side and render them into the client-side output during **runtime** instead. This makes the compiled code location-independent and you don't need to recompile the code if you deploy it to another location. To make this possible, `react-scripts-with-ssr` makes some significant changes:
56-
* The `public/index.html` template is supposed to contain a `<base href="%PUBLIC_URL%/" />` tag
57-
* Urls to other included files in `public/index.html` may be relative and will work in combination with the base tag
58-
* The variable substitution of `%PUBLIC_URL%` must take place during runtime inside the ssr request handler.
59-
* On the client-side, the base tag href value is provided as `process.env.PUBLIC_URL`
60-
* Other environment variables (NODE_ENV and the ones with REACT_APP prefix) are still baked in at compile time
61+
### Using runtime environment variables on the client
6162

62-
### Transferring ssr data to the client
63+
`create-react-app` provides `process.env` on the client-side to access environment variables prefixed with REACT_APP and defined at **compile time**.
6364

64-
If you need to "transfer" values from the ssr request handler to the client (e.g. for preloaded state), you can do this:
65-
* Add a variable assignment script `<script>FOO='%FOO%'</script>` to `public/index.html`
66-
* Replace the placeholder in the ssr request handler with `.replace(/%FOO%/g, foo)`
65+
However, if you want to make your server's **runtime** environment variables available on the client-side, you can do this with a `clientEnv` variable:
66+
* Add a variable assignment script `<script>clientEnv=%CLIENT_ENV%</script>` to the head section in `public/index.html`.
67+
* Replace the placeholder in the ssr request handler with `.replace(/%CLIENT_ENV%/g, JSON.stringify({ FOO: 'bar' }))`.
68+
* Access the value on the client-side with `clientEnv.FOO`.
69+
* For TypeScript: Declare the global variable `declare const clientEnv: NodeJS.ProcessEnv;`.
70+
71+
You might also use this mechanism to transfer preloaded state to the client.
6772

6873

6974
## Contribute

template-typescript/public/index.html

+12-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,17 @@
22
<html lang="en">
33

44
<head>
5-
<base href="%PUBLIC_URL%/" />
5+
<!--
6+
%BASE_HREF% is a placeholder, that is supposed to be replaced by the
7+
server-side renderer. %BASE_HREF% is determined during runtime and can be
8+
changed without recompiling making your compiled web app independent of
9+
a server / CDN location. The base tag specifies how to resolve relative
10+
URLs for CSS files, favicon, etc (notice the trailing slash in the href
11+
attribute). Therefore, using %BASE_HREF% only makes sense if all other URL
12+
references are kept relative. Alternatively, you can delete the base tag
13+
and use %PUBLIC_URL% as documented by create-react-app.
14+
-->
15+
<base href="%BASE_HREF%/" />
616
<meta charset="utf-8" />
717
<link rel="shortcut icon" href="favicon.ico" />
818
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
@@ -12,15 +22,7 @@
1222
homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
1323
-->
1424
<link rel="manifest" href="manifest.json" />
15-
<!--
16-
Notice the use of %PUBLIC_URL% in the tags above.
17-
It will be replaced with the URL of the `public` folder during the build.
18-
Only files inside the `public` folder can be referenced from the HTML.
19-
20-
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
21-
work correctly both with client-side routing and a non-root public URL.
22-
Learn how to configure a non-root public URL by running `npm run build`.
23-
-->
25+
<script>clientEnv=%CLIENT_ENV%</script>
2426
<title>React App</title>
2527
</head>
2628

template-typescript/src/index.ssr.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ const ssrRegex = /^\/(?!static|favicon\.ico|.*hot-update\.js).*/;
2929
router.use(ssrRegex, (request: express.Request, response: express.Response, next: express.NextFunction) => {
3030
const template = readFileSync('build/index.html')
3131
.toString()
32-
.replace(/%PUBLIC_URL%/g, process.env.PUBLIC_URL || '');
32+
.replace(/%BASE_HREF%/g, process.env.BASE_HREF || '')
33+
.replace(/%CLIENT_ENV%/g, JSON.stringify({
34+
FOO: 'bar'
35+
}));
3336

3437
const [head, tail] = template.split('%ROOT%');
3538
const stream = renderToNodeStream(<App />);

template-typescript/src/index.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ import App from './App';
55
import './App.css';
66
import * as serviceWorker from './serviceWorker';
77

8+
// Declare global variable clientEnv
9+
declare const clientEnv: NodeJS.ProcessEnv;
10+
// Use clientEnv to access environment variables from the server.
11+
12+
// Get the server URL from the base tag if you need it.
13+
const baseHref = document.getElementsByTagName("base")[0].href;
14+
815
ReactDOM.render(<App />, document.getElementById('root'));
916

1017
// If you want your app to work offline and load faster, you can change

template/public/index.html

+12-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,17 @@
22
<html lang="en">
33

44
<head>
5-
<base href="%PUBLIC_URL%/" />
5+
<!--
6+
%BASE_HREF% is a placeholder, that is supposed to be replaced by the
7+
server-side renderer. %BASE_HREF% is determined during runtime and can be
8+
changed without recompiling making your compiled web app independent of
9+
a server / CDN location. The base tag specifies how to resolve relative
10+
URLs for CSS files, favicon, etc (notice the trailing slash in the href
11+
attribute). Therefore, using %BASE_HREF% only makes sense if all other URL
12+
references are kept relative. Alternatively, you can delete the base tag
13+
and use %PUBLIC_URL% as documented by create-react-app.
14+
-->
15+
<base href="%BASE_HREF%/" />
616
<meta charset="utf-8" />
717
<link rel="shortcut icon" href="favicon.ico" />
818
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
@@ -12,15 +22,7 @@
1222
homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
1323
-->
1424
<link rel="manifest" href="manifest.json" />
15-
<!--
16-
Notice the use of %PUBLIC_URL% in the tags above.
17-
It will be replaced with the URL of the `public` folder during the build.
18-
Only files inside the `public` folder can be referenced from the HTML.
19-
20-
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
21-
work correctly both with client-side routing and a non-root public URL.
22-
Learn how to configure a non-root public URL by running `npm run build`.
23-
-->
25+
<script>clientEnv=%CLIENT_ENV%</script>
2426
<title>React App</title>
2527
</head>
2628

template/src/index.js

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import App from './App';
55
import './App.css';
66
import * as serviceWorker from './serviceWorker';
77

8+
// Use clientEnv to access environment variables from the server.
9+
10+
// Get the server URL from the base tag if you need it.
11+
const baseHref = document.getElementsByTagName("base")[0].href;
12+
813
ReactDOM.render(<App />, document.getElementById('root'));
914

1015
// If you want your app to work offline and load faster, you can change

template/src/index.ssr.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ const ssrRegex = /^\/(?!static|favicon\.ico|.*hot-update\.js).*/;
2929
router.use(ssrRegex, (request, response, next) => {
3030
const template = readFileSync('build/index.html')
3131
.toString()
32-
.replace(/%PUBLIC_URL%/g, process.env.PUBLIC_URL || '');
32+
.replace(/%BASE_HREF%/g, process.env.BASE_HREF || '')
33+
.replace(/%CLIENT_ENV%/g, JSON.stringify({
34+
FOO: 'bar'
35+
}));
3336

3437
const [head, tail] = template.split('%ROOT%');
3538
const stream = renderToNodeStream(<App />);

0 commit comments

Comments
 (0)