Routing to static HTML using React and AWS-Amplify - reactjs

I have built a website using React and am now using AWS-Amplify to host it. I am trying to setup Routes that redirect to static HTML files which are located in the /public directory. Everything works just fine when I test locally, but the Routes don't work after the site has been deployed.
This is what I have for my Routes.
<BrowserRouter>
<Routes>
.
. // Other unrelated Routes here..
.
<Route path="/page1" render={() => {window.location.href="page1/index.html"}} />
<Route path="/page2" render={() => {window.location.href="page2/index.html"}} />
<Route path="/page3" render={() => {window.location.href="page3/index.html"}} />
</Routes>
</BrowserRouter>
My rewrites and redirects setting for 200 (Rewrites) is currently:
</^[^.]+$|\.(?!(html|css|gif|ico|jpg|jpeg|js|png|PNG|txt|svg|woff|ttf|map|json)$)([^.]+$)/>
The console doesn't give any warnings or errors whenever I try to access these static HTML files from the deployed site, but a null page is loaded. Is there some settings I need to modify on my Amplify application? Thanks!

Try removing your react-router entries and adding some rewrites/redirects in amplify console for something like:
/page1 /public/page1/index.html 200
/page2 /public/page2/index.html 200
This may give you some ideas for a solution using rewrites/redirects. I've used it myself but not sure on how maintainable it is going forward.

From the AWS Amplify console left sidebar under App Settings, click Rewrites and redirects.
click "Edit" on the right-hand side and in the same place click "Open text editor"
your configurations should be something like the below.
[
{
"source": "/static-folder-in-public/<*>",
"target": "/static-folder-in-public/<*>",
"status": "200",
"condition": null
},
{
"source": "/static/<*>",
"target": "/static/<*>",
"status": "200",
"condition": null
},
{
"source": "</^[^.]+$|\\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf|json|xml)$)([^.]+$)/>",
"target": "/",
"status": "200",
"condition": null
}
]

Related

React.js (Vite) application returns 404 when refreshed on route

Nevermind...
Mistake was related to the codebase (where I'm using monorepo and vercel.json was on the root directory. After moving vercel.js to package with react application everything was working perfectly.
The 404 Problem
I have a simple React.js application with wouter-based routing and Vercel deployments that drive me into a little issue where I have no idea about the solution. It always returns 404 after refresh (of subpage).
My code looks a bit like this.
<>
<Route path='/' component={Intro} />
<Route path='/dashboard' component={Dashboard} />
</>
Where on <Intro /> I have <Link /> to /dashboard which should transfer me to <Dashboard /> page. And it does work on my local machine, in a container, and on Linux-based deployment, but it doesn't really work in vercel deployment, even if I tried to resolve this issue with the configuration of vercel.json.
{
"github": {
"silent": true
},
"rewrites": [
{
"source": "(.*)",
"destination": "/index.html"
}
]
}
Also tried an alternative version of rewrites and still the same issue.
{
"rewrites": [{ "source": "/(.*)", "destination": "/" }]
}
Link to live deployment available here: pointrest-6ttd9zep8-araclx.vercel.app
NOTE: I was also trying to use react-router but the same problem exists. Same problem exist when application is hosted on netlify but doesn't exist at all when hosted on heroku or run inside docker container.
You can a create simple rewrites rules in the serve. In my case I use Vercel. Then you can find something similar this.
Create in the project root a file vercel.json
And writer
{
"rewrites": [{ "source": "/(.*)", "destination": "/" }]
}
P.S.: Don't forget to enable rewrites in vercel.
TLDR
Add an empty 404.html in the public folder (you can put the title in the title tag) with this script in the head section
<script type="text/javascript">
var pathSegmentsToKeep = 0;
var l = window.location;
l.replace(
l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' +
l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +
(l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +
l.hash
);
</script>
Then add this script to your index.html
<script type="text/javascript">
(function(l) {
if (l.search[1] === '/' ) {
var decoded = l.search.slice(1).split('&').map(function(s) {
return s.replace(/~and~/g, '&')
}).join('?');
window.history.replaceState(null, null,
l.pathname.slice(0, -1) + decoded + l.hash
);
}
}(window.location))
</script>
This worked perfectly for me after deploying my app to render.com
To handle the "real" not found response, you can add this route
<Route path="*" element={<p>Page not found</p>} />
For more information you can visit this repo spa-github-pages
Credits to #rafgraph

Why does react-router not works at vercel?

I am trying to publish a serverless web to vercel.
I want to use react-router and this works good on my computer but when I deploy it It doesn't works
Can somebody help me?
(I want to do it without server)
// My main code
import React from 'react'
import { BrowserRouter, Switch, Route } from 'react-router-dom'
import Main from './routes/Main'
import Tos from './routes/Tos'
import Privacy from './routes/Privacy'
import NotFound from './routes/NotFound'
import Recruit from './routes/Recruit'
const App = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path = '/' component = {Main} />
<Route exact path = '/recruit' component = {Recruit} />
<Route exact path = '/tos' component = {Tos} />
<Route exact path = '/privacy' component = {Privacy} />
<Route component = {NotFound} />
</Switch>
</BrowserRouter>
)
}
export default App
Add a vercel.json file at the root of your project, and use "rewrites" to rewrite all incoming paths to refer to your index path.
For example:
{
"rewrites": [
{"source": "/(.*)", "destination": "/"}
]
}
Check here for further information: https://vercel.com/docs/configuration#project/rewrites
Specifying each Route
In order to make the React Router work in Vercel I had to specify each route in the vercel.json file, as mentioned in Surbina's answer. (Thanks btw, I used this solution for a quite some time)
{
"routes": [
{ "src": "/", "dest": "/" },
{ "src": "/recruit", "dest": "/" }
{ "src": "/tos", "dest": "/" }
// ....
]
}
But this may not be optimal depending on how many routes your application has, and honestly sometimes I forget to add new routes.
The "Match all" regex problem in Vercel
I tried the "match all" Regex as the source route [{ "src": "/*", "dest": "/" }], like I used to do in other hosting services, but for some Reason, Vercel server uses this routing for all requests, even when index.html needs to request a file like bundle.js, breaking the application.
Solution: Match routes but ignore files
And the solution I've found is using a Regex that matches all routes, except for routes that include a dot (.), which are usually files, like bundle.js. Then you can request a url like /recruit and it gets routed to / since it doesn't have a dot.
The regex is /[^.]+ (Turns into ^/[^.]+$ on Vercel).
Please note that this is a very simple Regex and may not cover all cases, however, I haven't got any issues with it to the moment of this comment.
So my vercel.json file looks like this:
{
"routes": [{ "src": "/[^.]+", "dest": "/", "status": 200 }]
}
I hope it is useful for you!
Simply add this to your vercel.json, this will route all your routes to the index.html except for the js file
{
"routes": [
{
"src": "/[^.]+",
"dest": "/"
}
]
}
In vercel hosting service you can either deploy the whole project by selecting create-react-app in framework preset :
or you can deploy only the build folder after you've built the project by running npm run build and select Other as Framework preset :
Note
If you're using react-router-dom with BrowserRouter you should have vercel.json file at the root of your project folder or your build folder :
{
"rewrites": [
{
"source": "/((?!api/.*).*)",
"destination": "/index.html"
}
]
}
This configuration will prevent your web application to have 404 error when you refresh the page or when you open other routes .
Hope it helped you .
Maybe you need the server to redirect all requests to index.html. If Apache then you can use mod_rewrite and you would need some code like this:
RewriteCond %{REQUEST_URI} !^/index\.html
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/.*$ /index.html [L,PT]
It sounds like Vercel is looking in the wrong place for your files. Check the "homepage" field in your package.json; I've found that they're finicky, and sometimes what works when using localhost doesn't work on a Vercel deployment. If you don't have a `"homepage" field, start by adding
"name": "my-app",
"homepage": ".",
...
in package.json. That usually works for me, but if it doesn't, try the actual url of your deployment, i.e., https://something.vercel.app. I have had to do that for certain setups.
If that still doesn't work, check the Network tab in devtools and find your request, and check what url it's actually looking for resources with. That might give you some clues.
I'm using Create React App and react-scripts for building the project and I had an issue similar issue. The problem was the sub-routes content wasn't loading when I click browser refresh on the vercel's deployed site but locally it works fine.
ex. if you go directly to this sub route it would failed
https://mysite/top/second
It turns out that the problem is in my package.json I had "homepage": "." I simply fixed it by changing it "homepage": "" (removing the dot)
react-scripts build will read package.json homepage property and it will append the value from homepage to all the paths that links the js and css bundled code.
You can also run (your production build) like
yarn serve build -s
-s option redirects all not found paths to index.html, so you don't even need the serve.json/vercel.json file

How to use a wild card for BrowserRouter and a Phoenix project

I was hoping to get some help on understand how to use BrowserRouter with Phoenix. When I first created the project, I decided not to use any html (controllers/views) and fully depend on GraphQL/React. Resulting in my routes to be
pipeline :api do
plug :accepts, ["json"]
end
scope "/" do
pipe_through :api
forward "/api", Absinthe.Plug,
schema: HuntersWeb.Schema.Schema
forward "/graphiql", Absinthe.Plug.GraphiQL,
schema: HuntersWeb.Schema.Schema,
socket: HuntersWeb.UserSocket
end
The problem I'm getting is when I use BrowserRouter and for example try to access /signup, instead of the component being run I get a Cannot Get /signup. I was hoping to get some advice on how to use a wild card to solve this problem? Would I need to create a controller/view in order for it to work?
All help, advice and pointing in the right direction to better understand this approach and solution to the problem is appreciated :) If more information is needed please ask as well.
React Code
const App = () => (
<Router>
<Switch>
<Route exact path="/" component={Landing} />
<Route exact path="/signup" component={Signup} />
</Switch>
</Router>
);
UPDATE
Client.js
const HTTP_ENDPOINT = "http://localhost:4000/api";
const WS_ENDPOINT = "ws://localhost:4000/socket";
// Create an HTTP link to the Phoenix app's HTTP endpoint URL.
const httpLink = createHttpLink({
uri: HTTP_ENDPOINT
});
Config.exs
config :cors_plug,
origin: ["http://localhost:3000"],
max_age: 86400,
methods: ["GET", "POST"]

react-helmet with react-rails server side rendering

I'm trying to use react-helmet with react-rails and webpacker with server-side rendering enabled to enable handling the generation of meta tag when user navigates on different routes. I'm able to generate the page on the server and get the content of the page, but the meta tags react-helmet is responsible for generating them are missing. Although on the client side react-helmet does is job perfectly, on the server side the meta tags are not delivered. Is there any reason why react-helmet is not being executed on the server side while the whole other parts of the application does ?
// App.js
// if window defined else ...
<StaticRouter location={this.props.path} context={context}>
<Route path='/' render=>{ routerProps => <Landing {...routerProps}/>}/>
</StaticRouter>
// Landing
// render ...
<section>
<Helmet
title={'Title'}
meta={[
{"name": "robots", "content": "index, follow"},
{"property": "og:site_name", "content": ""},
{"property": "og:title", "content": ''},
{"property": "og:type", "content": "website"},
{"property": "og:url", "content": this.props.path },
{"property": "og:description", "content": '...'},
]}
/>
...
</section>
// views/main.slim
= react_component "App", {} , { prerender: true }
// app/javascript/packs/application.js
var componentRequireContext = require.context("my_app", true)
var ReactRailsUJS = require("react_ujs")
ReactRailsUJS.useContext(componentRequireContext)
Any help would be greatly appreciated. Thanks.

Redirect issue on a firebase hosted site, not on localhost

I'm using the este boilerplate for a project using React, Redux and Firebase.
I want a similar functionality to Facebook regarding the root path '/', i.e. you see your news feed on the root path if you are authenticated, but if not, you have to sign in.
More specifically, I want the root path to render GamesPage (similar to news feed) if user is authenticated, but if not, then redirect to path '/signin'.
The below code works fine on localhost, i.e. if I'm not signed in (authenticated) and go to localhost:3000/ I immediately redirect to localhost:3000/signin. If I however was authenticated, the GamesPage is rendered on localhost:3000/.
However, for some reason this does not work on our https://[not-the-real-name].firebaseapp.com/, so i'm thinking this is a Firebase problem. What happens there is this error saying:
Moved Permanently. Redirecting to /signin
This happens on any path, '/', '/signin' (as in picture), etc. and I have no clue why.
In my src/browser/app/App.js file I have the following lines:
<Match authorized exactly pattern="/" component={GamesPage} />
<Match pattern="/signin" component={SignInPage} />
<Match authorized pattern="/users" component={UsersPage} />
<Match authorized pattern="/me" component={MePage} />
<Miss component={NotFoundPage} />
...
Match.js is the same as in este boilerplate: src/common/app/components/Match.js
SignInPage render function begins like this:
render() {
return (
this.props.viewer ?
<Redirect
to={(
this.props.location.state &&
this.props.location.state.from &&
this.props.location.state.from.pathname
) || '/'}
/>
:
<View className="sign-in">
...
I don't know what other code I should add to the question. Any help appreciated.
EDIT:
As per request, I've made a live repro, can be seen here: testeste-690e9.firebaseapp.com. This is actually the este boilerplate, with only 1 change, I changed <Match exactly pattern="/" component={HomePage} /> in src/browser/app/App.js to <Match authorized exactly pattern="/" component={HomePage} />. There seems to be a problem with having the root authorized.
EDIT 2:
firebase.json contents:
{
"database": {
"rules": "./firebase/rules.bolt"
},
"hosting": {
"public": "build",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers": [
{
"source" : "**/*.#(eot|otf|ttf|ttc|woff|font.css)",
"headers" : [{
"key" : "Access-Control-Allow-Origin",
"value" : "*"
}]
}, {
"source" : "**/*.#(jpg|jpeg|gif|png)",
"headers" : [{
"key" : "Cache-Control",
"value" : "max-age=7200"
}]
}
],
"cleanUrls": true,
"trailingSlash": false
}
}

Resources