Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • C create-react-app
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 1,547
    • Issues 1,547
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 417
    • Merge requests 417
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Meta
  • create-react-app
  • Merge requests
  • !703

Add support for public/ folder

  • Review changes

  • Download
  • Email patches
  • Plain diff
Merged Administrator requested to merge public-folder into master Sep 22, 2016
  • Overview 10
  • Commits 1
  • Pipelines 0
  • Changes 12

Created by: gaearon

I’ve been thinking about this for a few weeks and I have a solution for static assets that satisfies me. It is not exactly what we discussed before so please bear with me and give it five minutes.

Problem

Currently, the only way to use an image or another resource is to import it from JavaScript. This works really well for most use cases because Webpack takes care of:

  • Raising a compilation error if the file is missing.
  • Adding a content hash to the filename so if file changes, browser cache is busted.

However there are a few problems with this approach. Let me group them into a few buckets:

  • [ROOT-NAMES] Some files (e.g. favicon.ico or manifest.json) must exist at top level. Default approach of putting them into static/ and adding a content hash doesn’t work for them and we have to add exceptions. (https://github.com/facebookincubator/create-react-app/issues/558, https://github.com/facebookincubator/create-react-app/issues/697)
  • [ANY-ATTRIBUTES] HTML needs to be able to refer to asset paths inside any attributes. For example, some <meta> tags may want to refer to an image URL in their content, but we can’t be sure which ones. (https://github.com/facebookincubator/create-react-app/issues/618)
  • [DYNAMIC-FILES] Sometimes you have hundreds of image files that rarely change, and you want to do something dynamic instead of making hundreds of imports. (https://github.com/facebookincubator/create-react-app/issues/585#issuecomment-244809341)
  • [SKIP-BUNDLER] Sometimes you really want to load some JS or CSS independently of the bundler, for example, to show a spinner, or to use a script that somehow breaks Webpack. (https://github.com/facebookincubator/create-react-app/issues/625, https://github.com/facebookincubator/create-react-app/issues/573#issuecomment-245934473)

This means we need an alternative way of including some assets into the build.

Constraints

  • [PROD-WORKS] If it works in development, it must work in production too.
  • [HOMEPAGE] User may specify a custom homepage in package.json, and we should respect that.
  • [ROUTING] User may use client-side routing, and app may be served from an arbitrary subpath.
  • [SECRETS] It should be obvious to the user which files or folders will end up in the build output.
  • [PIT-OF-SUCCESS] There should be one relatively obvious way to do it.

Prior Solutions

Implicitly Serve Everything in Development

This is how it worked prior to 0.4.0. It wasn’t intentional, but WebpackDevServer defaults to this behavior.

This means <img src="/src/logo.svg"> or <link rel="shortcut icon" href="/favicon.ico"> happened to work in development. However once you compile them with npm run build, the links would be broken because module system knew nothing about those files.

Verdict: solves all problems but violates [PROD-WORKS].

Auto-Detect Assets in HTML

This is the current solution that was added as a stop-gap measure in 0.4.0. Any <link href> attribute is being parsed, and relative URLs like ./favicon.ico get processed through Webpack.

Verdict: does not violate the constraints but also does not solve [ROOT-NAMES] unless we keep an extensive whitelist. JSON files make it nearly impossible to keep a whitelist like this because they should be treated differently depending on whether they’re imported from HTML or JS (and Webpack doesn’t support this). Similarly, [ANY-ATTRIBUTES], [DYNAMIC-FILES], and [SKIP-BUNDLER] are unsolved.

Introduce a Static Folder and Serve It

This is the approach described in https://github.com/facebookincubator/create-react-app/pull/226. If an additional /static folder exists, it is served under /static and is merged with /build/static on build.

Verdict: it does not solve [ROOT-NAMES] unless we merge static/* directly into build/* output. However, in that case, for something to be served from /static/, you’d have to create static/static which is confusing and violates [PIT-OF-SUCCESS]. It also violates [PIT-OF-SUCCESS] because to refer to something, you’d have to use /static/* paths in your HTML, but to many people, this would imply that /src/* or /img/* would work the same way. Most importantly, it violates [HOMEPAGE] because there is no way for us to fix /static/ path to have the right absolute prefix without parsing HTML and applying some brittle heuristics. It would also be confusing that /static/ paths get “auto-fixed” in HTML while other paths are kept intact. Finally, if we proposed that user types relative static/* in HTML instead, it would violate [ROUTING].

Proposed Solution

There is a new top-level folder called public. We move index.html and favicon.ico there.

We introduce a new concept called “public URL”. It roughly corresponds to “public path” in Webpack. It is already configurable via homepage field in package.json, but now we also expose it to the user.

In HTML:

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

In JavaScript:

render() {
  // Escape hatch! Normally you’d just import them.
  const src = `${process.env.PUBLIC_URL}/img/team_/${this.props.teamId}.png`;
  return <img src={src} alt='Team' />;
}

Only files inside public folder are accessible by public URL. You can’t refer to things from node_modules or src. The public folder contents gets merged with the build output.

Let’s see how this proposal addresses each of the problems and constraints.

Addressed Problems

[ROOT-NAMES] Some files (e.g. favicon.ico or manifest.json) must exist at top level. Default approach of putting them into static/ and adding a content hash doesn’t work for them and we have to add exceptions. (https://github.com/facebookincubator/create-react-app/issues/558, https://github.com/facebookincubator/create-react-app/issues/697)

With public folder, you have full control over how to call your files.

[ANY-ATTRIBUTES] HTML needs to be able to refer to asset paths inside any attributes. For example, some <meta> tags may want to refer to an image URL in their content, but we can’t be sure which ones. (https://github.com/facebookincubator/create-react-app/issues/618)

With %PUBLIC_URL%, you have full control over attributes.

[DYNAMIC-FILES] Sometimes you have hundreds of image files that rarely change, and you want to do something dynamic instead of making hundreds of imports. (https://github.com/facebookincubator/create-react-app/issues/585#issuecomment-244809341)

We expose process.env.PUBLIC_URL so you can do that now.

[SKIP-BUNDLER] Sometimes you really want to load some JS or CSS independently of the bundler, for example, to show a spinner, or to use a script that somehow breaks Webpack. (https://github.com/facebookincubator/create-react-app/issues/625, https://github.com/facebookincubator/create-react-app/issues/573#issuecomment-245934473)

Again, since we don’t process files in public folder with webpack, this will work.

Addressed Constraints

[PROD-WORKS] If it works in development, it must work in production too.

The public folder gets merged with the build output, so references both from HTML and JS will keep working in production.

[HOMEPAGE] User may specify a custom homepage in package.json, and we should respect that.

Both %PUBLIC_URL% and process.env.PUBLIC_URL take homepage into account, as they directly correspond to publicPath used by Webpack.

[ROUTING] User may use client-side routing, and app may be served from an arbitrary subpath.

Both %PUBLIC_URL% and process.env.PUBLIC_URL provide safety against that, as they are absolute and know about homepage.

[SECRETS] It should be obvious to the user which files or folders will end up in the build output.

The mental model is simple: only public files are merged with build output.

[PIT-OF-SUCCESS] There should be one relatively obvious way to do it.

You can’t accidentally refer to a file in src or node_modules because it will not work. We show two preferred methods out of the box: importing images in App.js, and using %PUBLIC_URL% in index.html. Using process.env.PUBLIC_URL is an escape hatch so we will only use mention in documentation for people who need it.


Overall I feel like it’s a solid, explicit solution, and I intend to release it as part of 0.5.0.

Assignee
Assign to
Reviewers
Request review from
Time tracking
Source branch: public-folder