128

I'm trying

PUBLIC_URL=http://example.com npm run build

with a project built using the latest create-react-script.

However, the occurrences of %PUBLIC_URL% in public/index.html are replaced with an empty string, not the expected value PUBLIC_URL.

public/index.html contains code like

<script src="%PUBLIC_URL%/static/js/jarvis.widget.min.js"></script>

Hours of searching the internet and stack overflow show that very little is written about PUBLIC_URL. I cloned create-react-app from GitHub and have been browsing the code but have not yet been enlightened.

Does anyone have any suggestions as to what I'm doing wrong?

Marko Kohtala
  • 744
  • 6
  • 16
Gulliver Smith
  • 1,511
  • 2
  • 11
  • 11

13 Answers13

90

If the other answers aren't working for you, there's also a homepage field in package.json. After running npm run build you should get a message like the following:

The project was built assuming it is hosted at the server root.
To override this, specify the homepage in your package.json.
For example, add this to build it for GitHub Pages:

  "homepage" : "http://myname.github.io/myapp",

You would just add it as one of the root fields in package.json, e.g.

{
  // ...
  "scripts": {
    // ...
  },
  "homepage": "https://example.com"
}

When it's successfully set, either via homepage or PUBLIC_URL, you should instead get a message like this:

The project was built assuming it is hosted at https://example.com.
You can control this with the homepage field in your package.json.
redbmk
  • 4,687
  • 3
  • 25
  • 49
84

People like me who are looking for something like this in in build:

<script type="text/javascript" src="https://dsomething.cloudfront.net/static/js/main.ec7f8972.js">

Then setting https://dsomething.cloudfront.net to homepage in package.json will not work.

1. Quick Solution

Build your project like this:
(windows)

set PUBLIC_URL=https://dsomething.cloudfront.net&&npm run build

(linux/mac)

PUBLIC_URL=https://dsomething.cloudfront.net npm run build

And you will get

<script type="text/javascript" src="https://dsomething.cloudfront.net/static/js/main.ec7f8972.js">

in your built index.html

2. Permanent & Recommended Solution

Create a file called .env at your project root(same place where package.json is located).
In this file write this(no quotes around the url):

PUBLIC_URL=https://dsomething.cloudfront.net

Build your project as usual (npm run build)
This will also generate index.html with:

<script type="text/javascript" src="https://dsomething.cloudfront.net/static/js/main.ec7f8972.js">

3. Weird Solution (Will do not work in latest react-scripts version)

Add this in your package.json
"homepage": "http://://dsomething.cloudfront.net",

Then index.html will be generated with:

<script type="text/javascript" src="//dsomething.cloudfront.net/static/js/main.ec7f8972.js">

Which is basically the same as:

<script type="text/javascript" src="https://dsomething.cloudfront.net/static/js/main.ec7f8972.js">

in my understanding.

Github Issue Github Comment

Gautam Krishna R
  • 2,388
  • 19
  • 26
Vaibhav Vishal
  • 6,576
  • 7
  • 27
  • 48
  • 1
    I'm not really sure why they closed the issue. Only solution that works for me is the third one you suggest and it indeed seems really weird. Therefore I still think this is a bug. Or can someone explain to me the meaning of last comment before the issue was closed? – Maroš Beťko Jan 18 '19 at 15:32
  • 1
    It will not work in the latest react scripts version, please use the .env approach. – Gautam Krishna R Jul 01 '20 at 11:02
  • @Vaibhav Hi! I having facing a different problem. I want to change the `/static/js` path to `/static/dashboard/js/`. But I can't find any solution on the internet about how to do this. Any idea? I have even asked a question too. Check here https://stackoverflow.com/questions/66655025/how-to-change-production-build-static-folder-path-in-a-react-project?noredirect=1#comment117828879_66655025 – Souvik Ray Mar 16 '21 at 14:29
  • 1
    This answer is a solution to the react bug described in https://github.com/facebook/create-react-app/issues/8813. I can't thank you enough for this. – Bash Aug 30 '21 at 04:46
  • Thanks for this answer. I tried the .env file and it didn't work at first. I realized that i was saving it as a .txt extension by mistake so anyone reading make sure when you're saving your .env file you choose (on windows) Save as type: "All Files". Maybe you could add this to the answer for people reading in future? – Sarah May 10 '22 at 14:12
  • how this will work in css? how can i add font files? – Sunil Garg Sep 12 '22 at 06:54
28

That is not how the PUBLIC_URL variable is used. According to the documentation, you can use the PUBLIC_URL in your HTML:

<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">

Or in your JavaScript:

render() {
  // Note: this is an escape hatch and should be used sparingly!
  // Normally we recommend using `import` for getting asset URLs
  // as described in “Adding Images and Fonts” above this section.
  return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;
}

The PUBLIC_URL is not something you set to a value of your choosing, it is a way to store files in your deployment outside of Webpack's build system.

To view this, run your CRA app and add this to the src/index.js file:

console.log('public url: ', process.env.PUBLIC_URL)

You'll see the URL already exists.

Read more in the CRA docs.

Toby
  • 12,743
  • 8
  • 43
  • 75
  • 2
    "The PUBLIC_URL is not something you set to a value of your choosing". Are you sure about that? https://create-react-app.dev/docs/advanced-configuration -> "You can adjust various development and production settings by setting environment variables in your shell or with .env". PUBLIC_URL -> "You may use this variable to force assets to be referenced verbatim to the url you provide (hostname included). This may be particularly useful when using a CDN to host your application." – tokland Jul 02 '22 at 15:59
  • 1
    The responder is right. CRA framework wants you edit app context path via package.json `homepage` and `PUBLIC_URL` receives that value. They opened it for altering and currently I imaging the only case when instead of the **relative context path** you want to embed a **fixed protocol / domain / port** (to make it difficult to clone your app??) Customization should be externalized somehow, and `PUBLIC_URL` is here for your build pipelines. – gavenkoa Feb 21 '23 at 22:29
11

Actually the way of setting environment variables is different between different Operating System.

Windows (cmd.exe)

set PUBLIC_URL=http://xxxx.com&&npm start

(Note: the lack of whitespace is intentional.)

Linux, macOS (Bash)

 PUBLIC_URL=http://xxxx.com npm start

Recommended: cross-env

{
  "scripts": {
    "serve": "cross-env PUBLIC_URL=http://xxxx.com npm start"
  }
}

ref: create-react-app/README.md#adding-temporary-environment-variables-in-your-shell at master · facebookincubator/create-react-app

Luis Gouveia
  • 8,334
  • 9
  • 46
  • 68
JimmyLv
  • 371
  • 5
  • 11
9

Have a look at the documentation. You can have a .env file which picks up the PUBLIC_URL

Although you should remember that what its used for -

You may use this variable to force assets to be referenced verbatim to the url you provide (hostname included). This may be particularly useful when using a CDN to host your application.

johndodo
  • 17,247
  • 15
  • 96
  • 113
Dave
  • 127
  • 1
  • 7
9

If you see there source code they check if process.env.NODE_ENV === 'development' returns true, and they automatically removes host URL and only return path.

For example, if you set like below

PUBLIC_URL=http://example.com/static/

They will remove http://example.com and only return /static.

However since you only set root URL like http://example.com, they will just return an empty string since there no subpath in your URL string.

This only happens if you call react-scripts start, and if you call react-scripts build then isEnvDevelopment will be false, so it will just return http://example.com as what you set in the .env file.

Here is the source code of getPublicUrlOrPath.js.

/**
 * Returns a URL or a path with slash at the end
 * In production can be URL, abolute path, relative path
 * In development always will be an absolute path
 * In development can use `path` module functions for operations
 *
 * @param {boolean} isEnvDevelopment
 * @param {(string|undefined)} homepage a valid url or pathname
 * @param {(string|undefined)} envPublicUrl a valid url or pathname
 * @returns {string}
 */
function getPublicUrlOrPath(isEnvDevelopment, homepage, envPublicUrl) {
  const stubDomain = 'https://create-react-app.dev';

  if (envPublicUrl) {
    // ensure last slash exists
    envPublicUrl = envPublicUrl.endsWith('/')
      ? envPublicUrl
      : envPublicUrl + '/';

    // validate if `envPublicUrl` is a URL or path like
    // `stubDomain` is ignored if `envPublicUrl` contains a domain
    const validPublicUrl = new URL(envPublicUrl, stubDomain);

    return isEnvDevelopment
      ? envPublicUrl.startsWith('.')
        ? '/'
        : validPublicUrl.pathname
      : // Some apps do not use client-side routing with pushState.
        // For these, "homepage" can be set to "." to enable relative asset paths.
        envPublicUrl;
  }

  if (homepage) {
    // strip last slash if exists
    homepage = homepage.endsWith('/') ? homepage : homepage + '/';

    // validate if `homepage` is a URL or path like and use just pathname
    const validHomepagePathname = new URL(homepage, stubDomain).pathname;
    return isEnvDevelopment
      ? homepage.startsWith('.')
        ? '/'
        : validHomepagePathname
      : // Some apps do not use client-side routing with pushState.
      // For these, "homepage" can be set to "." to enable relative asset paths.
      homepage.startsWith('.')
      ? homepage
      : validHomepagePathname;
  }

  return '/';
}
wonsuc
  • 3,498
  • 1
  • 27
  • 30
6

As documented here create-react-app will only import environment variables beginning with REACT_APP_, so the PUBLIC_URL, I believe, as mentioned by @redbmk, comes from the homepage setting in the package.json file.

TobyG
  • 1,692
  • 3
  • 21
  • 36
3

Not sure why you aren't able to set it. In the source, PUBLIC_URL takes precedence over homepage

const envPublicUrl = process.env.PUBLIC_URL;
...
const getPublicUrl = appPackageJson =>
  envPublicUrl || require(appPackageJson).homepage;

You can try setting breakpoints in their code to see what logic is overriding your environment variable.

dosentmatter
  • 1,494
  • 1
  • 16
  • 23
3

This problem becomes apparent when you try to host a react app in github pages.

How I fixed this,

In in my main application file, called app.tsx, where I include the router. I set the basename, eg, <BrowserRouter basename="/Seans-TypeScript-ReactJS-Redux-Boilerplate/">

Note that it is a relative url, this completely simplifies the ability to run locally and hosted. The basename value, matches the repository title on GitHub. This is the path that GitHub pages will auto create.

That is all I needed to do.

See working example hosted on GitHub pages at

https://sean-bradley.github.io/Seans-TypeScript-ReactJS-Redux-Boilerplate/

Sean Bradley
  • 3,254
  • 1
  • 17
  • 10
2

This solution works on a Linux bash command line:

  1. Go to your react project root directory.
  2. open the bash terminal on that directory.
  3. After that run below two commands and you will see that the PUBLIC_URL changed:
> export PUBLIC_URL=https://example.com
> npm run build
1

If the PUBLIC_URL environment variable has no effect, it may be due to this issue, which causes PUBLIC_URL to be ignored in development. It was fixed in create-react-app version 3.4.0.

Aryan
  • 3,338
  • 4
  • 18
  • 43
Grant Zvolsky
  • 483
  • 5
  • 6
0

If your app has routing, try like below

  1. Create a file called .env at your project root where package.json is located, then add your production URL like below

    PUBLIC_URL=https://something.com
    
  2. add basename to BrowserRouter in the route configure (in App.js/ App.jsx)

    <BrowserRouter basename="https://something.com">
    
Gopala Raja Naika
  • 2,321
  • 23
  • 18
0

First of all the react compiled code is "pure java script". As such, from an html page, it is possible to reference this code using something like:

< script src="my_compiled_component.js" >< /script >

The creation of react components must not depend on the main website hyperlink. From react code, to locate the website your components are running from use something like:

window.location.protocol + '//'+ window.location.hostname + ':' + window.location.port

Following this pattern it is important to understand that some folders might need to have modified permissions so everybody can find your components through html calls. How to define where to place the compiled react code? This could be done from manually to using something like webpack that allows you to set this output.

Jose Cabrera Zuniga
  • 2,348
  • 3
  • 31
  • 56