Trouble with deep routes on a single-page app on Firebase hosting - reactjs

(Updated below with a little more info)
We have a react-based single-page web app that lives on Firebase Hosting. We've been using hash-based routing up to this point (e.g. mysite.com/#/path/to/content). We introduced React Router, which allows us to have prettier URLs (e.g. mysite.com/path/to/content), but now the app doesn't load when we navigate directly to a deep route. Details below:
Using React Router required us to use URL Rewriting in Firebase Hosting. The directions are pretty straightforward--it seems like all you need to do is this:
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
...inside the firebase.json file. In fact, our complete firebase.json file is as follows:
{
"firebase": "",
"public": "dist",
"rules": "rules.json",
"ignore": [
"firebase.json",
"**/.*",
"**/*.map",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
Nothing too complicated there (we're doing firebase deploy -f in our build script, if you're wondering why the firebase argument is empty). The linked-to rules.json file is even simpler:
{
"rules":{
".write":"true",
".read":"true"
}
}
When I load the app by going to the base URL and start navigating around, everything is fine. If I go directly to a route at one level deep (e.g. mysite.com/path), that works too.
HOWEVER
If I go directly to a route at a deep level, or even to a top-level route with a trailing slash (e.g. mysite.com/path/ or mysite.com/path/to/content), then the app doesn't load.
Specifically, what happens is the index.html page loads, and then browser goes to load our webpack-created bundle.js file that is referenced in index.html, but what Firebase Hosting returns for that JS file is the content of the index.html file. So then, predictably, what we see in the browser console is this error:
Uncaught SyntaxError: Unexpected token <
...on line 1 of the "bundle file". If I view the content of the bundle file in Chrome's dev tools, line 1 of the bundle file is literally this:
<!doctype html>
...which is then followed by the rest of the HTML from the index.html file.
It seems like what's going on is the Firebase URL rewrite rule is saying "oh, you're trying to reference a JS file--I see I'm supposed to return the content of index.html for everything, so here you go". But that can't be the case all the time, or the site would never work under any circumstances. So why does it work if I start at the site root, but breaks if I paste in a deep URL to the app? I have zero idea.
If it helps, here's how my index.html file references the bundle file:
<script src="bundle.ce843ef7a2ae68e9e319.js"></script></body>
So it seems like a problem with Firebase hosting, but I could also see that maybe I don't understand React Router at some level, and so I've somehow screwed things up with the client-side code in such a way that it's forcing the server to return the wrong thing.
Here's me hoping it's some stupid configuration thing we're missing. Any help is appreciated!
Thanks!
UPDATE: I stripped down our app so that it only returns "hello, world", which bypasses all of the React-based stuff, including React Router, and the problem persists, so I no longer think this has anything to do with React-Router, though I suppose there's still a small chance this could have something to do with React.

I heard back from Firebase Hosting. Apparently, the referenced bundle.js file in our index file needed a slash in front of it, to make it an absolute path. So this:
<script src="/bundle.js"></script>
...instead of this:
<script src="bundle.js"></script>
In case anybody else makes this same silly mistake, I hope this is useful.

[tl;dr]
This answer is only for educational purposes and in-depth insight to
the problem statement and its solution. For copy/paste purposes see
this answer provided by #hairbo. It is the short version and
answers the question simply and nicely
Every link to resources like images, css, js can be referenced to in 3 ways:
1) Absolute Path
<script src="http://app-url.com/bundle.js"></script>
This is absolute path and resource will be loaded from the specified path without pre-processing the path provided.
2) Relative Path
<script src="bundle.js"></script>
This is relative path and it refers to the resource file as if it is located in current working directory. That is when you are on app-url.com/lvl-1 the relative path becomes app-url.com/bundle.js which provides no problems because that is actually the path to resource.
Problem
But upon going another level deep, i.e. app-url.com/lvl-1/lvl-2, current working level becomes app-url.com/lvl-1/ and the relative path is treated as app-url.com/lvl-1/bundle.js but that is not actually the path to resource. Hence the index.html file mis-behaves because it can't load the required files.
3) Root Relative Path
<script src="/bundle.js"></script>
Adding a slash (/) before the relative path makes it root relative path. In this case all non-absolute paths are treated as paths rooted at the current domain. That is, in this case the path to resource /bundle.js is treated as app-url.com/bundle.js even if current working level of url is app-url/lvl-1/lvl-2/lvl-3/...
Solution Do not use ~relative paths~. Either use absolute paths or root relative paths in all files. This way every path will be treated
as expected.

I encountered the same behavior but the problem was unrelated to bundle.js.
My rewrites section of firebase.json looked like this:
"rewrites": [
{
"source": "**",
"destination": "index.html"
}
]
and I resolved the problem by using the root relative path instead of the relative path for index.html. (Thanks Ashu!)
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]

I had this same problem and I fixed adding rewrite rules to my firebase.json file.
here is how my firebase.json file looks.
{
"hosting": {
"public": "webApp/build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"cleanUrls": true,
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
},
"functions": {
"source": "functions"
}
}
sorry for my english, i still learning.

Related

Pages missing on the server side only, in a Firebase/React app

I have a little web app using Firebase and React.
Locally it runs on
http://localhost:3000/
and has a couple of subpages like:
http://localhost:3000/login
http://localhost:3000/manage
All is OK so far. Then I upload my page to the server (as I have already done a few times now), running:
npm run build
firebase deploy
I can at this point access the app on the server, as expected using some link like:
https://myapp.web.app/
But I hit problems hereafter, trying these URLs (for some subpages) in the browser:
https://myapp.web.app/login
https://myapp.web.app/manage
I get this result, showing that some pages must be missing:
404
Page Not Found
The specified file was not found on this website. Please check the URL for mistakes and try again.
Why am I seeing this?
This page was generated by the Firebase Command-Line Interface. To modify it, edit the 404.html file in your project's configured public directory.
I have checked my firebase.json file that this line is present in the "hosting" section:
"public": "build",
Knowing that, is there some possible issue that an experienced Firebase/React user would immediately think about and that I may be missing?
Your problem is probably because you init project and set it as it is not SPA.
Set up your firebase.json file like this:
{
"hosting": {
"target": "This is when you need target",
"public": "build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
Most important for SPA is "rewrites" section. When you open home page using browser request looks like this example.web.app/index.html even if you wrote example.web.app and when you request example.web.app/manage request looks like example.web.app/menage/index.html And in reallity you don't have this document so you need to setup hosting to rewrites all request to this one index.html document.

Firebase deploy --only hosting does not update React.js webapp content unless site data is cleared

I'm trying to update a react.js site that has been deployed to firebase. The updated content cannot be accessed without using an incognito window or using Dev Tools > Application > Clear Storage > Clear Site Data.
I'm sure this is a problem more strongly related to Firebase Hosting than React, but there seems to be potential interference between React's service-worker.js file and Firebase. Therefore, I have tagged React for completeness.
React Version: 16.8.6
Firebase CLI Version: 7.1.0
My steps are:
change code
$ npm run build
$ firebase serve --only hosting || firebase deploy --only hosting
visit .web.app or localhost:5000
Expectation: I will see the site with the new changes
Reality: I see the old site, without the new changes
Research and attempted solutions:
Setting the Cache-Control Headers
I have tried setting the Cache-Control headers to "no-cache" for service-worker.js in my firebase.json file.
{"source": "/service-worker.js", "headers": [{"key": "Cache-Control", "value": "no-cache"}]},
This is clearly mentioned in the Create React App deployment documentation here:
https://facebook.github.io/create-react-app/docs/deployment
And in numerous Stack Overflow questions, such as the one here:
Firebase hosting - force browser to reset cache on new deploys?
Unfortunately, the problem persists, even when my firebase.json file is as shown below. This has led me to believe that there may be a new, more recent issue.
Fiddling with the Service Worker:
I have also done research around the service worker itself. It is currently set to 'unregister', but I have set it to 'register' and deployed multiple times with no noticeable change.
I am unsure whether it must be set to 'register', given the advice in #1 above.
****firebase.json****
{
"hosting": {
"public": "build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers": [
{"source": "/service-worker.js", "headers": [{"key": "Cache-Control", "value": "no-cache"}]}
]
}
}
****/src/index.js****
.....
const store = configureStore();
....
serviceWorker.unregister();
****directory structure****
/
firebase.json
firebase.src
build/
/static
service-worker.js
index.html
....
....
In the end neither Reactjs nor Firebase Hosting was the source of the problem. I'm just posting this incase someone is in a similar situation and doubts the configuration above, its the correct one, use it.
Redux-Persist's Persist Gate was not working as expected, causing the app to have mini-crashes every time it was redeployed. The crashes occurred before things like the version number could be updated, making the site look like it hadn't received the update.
Clearing the cache would solve the Redux-Persist problem, as it clears the data in the local storage, but this made me think it was a browser cache issue when it wasn't.
If you're using Redux-Persist & Persist Gate and seeing a very similar issue, take a good look at whether Persist Gate is allowing components to render before rehydration. I solved it by using the _persist object in Redux as a flag
In case someone has the issue. I resolve it by removing the .firebase folder at my root folder and add it to my .gitignore.
It keep in cache the previously build version for making deployment faster.
I got some trouble with multi-sites configuration.

Correct way of subdomain configuration [duplicate]

We're using custom domains on firebase hosting: our app, served from index.html, runs nicely on app.example.com. We've also connected www.example.com, which serves the app as well.
The problem is: on www.example.com I want to be able to host a simple static page. Is there a way to configure this in the rewrites? We can easily output an extra html file in the deploy, next to index.html.
Any ideas?
Our firebase.json:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
Firebase Hosting does not support multiple sites in a single project at this time. However, you can create a second project for your static landing page and deploy it there, connecting it to the www domain while leaving the app domain connected to the other project.
As of August of 2018, Firebase Hosting supports multiple sites on a single project! See the docs for more info.

Implementing history-api-fallback for Webpack Production environment

My stack is React/Redux as frontend together with React Router v4 and Firebase Hosting as backend.
Like many others, I also faced the issue of meeting the 404 page not found when users refreshed at a page other than the root URL like https://example.com/about or manually key in the URL https://example.com/about.
I researched many websites and HashBrowser seem to be a workaround for this problem but it is not a complete solution. It does not bring the user to the correct page but instead render the root URL component. Not good enough. Using historyApiFallback: true for production environment seemed to be a bad idea too.
I saw this package, connect-history-api-fallback but it seems to be for a express app.
How can I implement this package in my React app while using Firebase to host my website?
Or are there other solutions?
I found the solution. This only applies to people deploying React App, using Firebase Hosting to host your single page application. (should work for Angularjs/Vuejs too)
When you first run firebase init, they ask if you want to configure as a single-page app, make sure you select yes. The end result is that they will write this portion to your firebase.json file,
"rewrites": [ {
"source": "**",
"destination": "/index.html"
}
it works the same as having
devServer: {
historyApiFallback: true
},
in your webpack.config.js file to redirect all URLs to the root URL of your application. ("/")
Full implementation of the firebase.json file may look like this,
{
"hosting": {
"public": "public",
"rewrites": [{
"source": "**",
"destination": "/index.html"
}],
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
More information may be found in the Firebase Deployment Configuration Documentation.

Having trouble using Firebase v3 to host my React sample

I am new to hosting React apps on Firebase, so any help would be appreciated. I've already followed the instructions on the new v3 documentations at Firebase.com, but I am unsure which assets/files to place into my public directory. I felt the documentation was quite vague in this regard.
Here is my Firebase.json:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
and my directory structure is below:
Name of App
-build
-css
-node_modules
-public
-scripts
.firebaserc
database.rules.json
firebase.json
gulpfile.js
index.html
package.json
README.md
I've already tried copying the entire contents of the parent directory into the public folder to no success. Any help would be appreciated.
So basically after messing around for a few hours I found out the solution. Since gulp minifies everything I needed to just push the main.js minified file under build along with the minified CSS folder into the public directory, and now everything works. Case closed.

Resources