I have gone through lot of similar questions in stack overflow like this one. Each one have different perspective of deploying the react app.
But this question is for those who use redux, react-router and react-router-redux. It took me lot of time to figure out the proper ways to host Single page application. I am aggregating all the steps here.
I have written the below answer that contains the steps to deploy react-redux app to S3.
You have deployed a React application in the AWS Cloud by integrating with GitHub and using AWS Amplify. With AWS Amplify, you can continuously deploy your application in the cloud and host it on a globally available CDN. Next, we will create a local version of the app to continue development and add new features.
This pattern provides a step-by-step approach and code to host a single-page application (SPA) written in React on Amazon Simple Storage Service (Amazon S3) and Amazon CloudFront. The pattern's sample SPA uses a REST API exposed by Amazon API Gateway to demonstrate cross-origin resource sharing (CORS) best practices.
They might also contain client-side scripts. By contrast, a dynamic website relies on server-side processing, including server-side scripts, such as PHP, JSP, or ASP.NET. Amazon S3 does not support server-side scripting, but AWS has other resources for hosting dynamic websites.
The S3 bucket is not a web server, so routing causes some issues. When using React router, routing works fine when you enter the site, but a 403 error is returned when opening an URL with a route resource, like mydomain.com/users .
There is a video tutorial for deploying react app to s3. This will be easy for deploying the app as such on s3 but we need to do some changes in our react app. Since it is difficult to follow, I am summarising as steps. Here it goes:
Add bucket policy, so that everyone can access the react app. click created bucket -> properties -> permissions. In that click Edit bucket policy. Here is an example for the bucket policy.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Allow Public Access to All Objects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<YOUR_BUCKET_NAME>/*"
}
]
}
If you want logs, create another bucket and set the bucket name under logs section in properties.
You need to have an index.html(which is the starting point of your app) and error.html and all the public assets(browserified reactJS app and other CSS, JS, img files) in a folder.
Make sure you have relative reference to all these public files in index.html.
Now, navigate to properties -> static website hosting. Enable the website hosting option. Add the index.html and error.html to the fields respectively. On saving, you will receive the Endpoint.
Finally, your react-redux app is deployed. All your react routes will work properly. But when you reload the S3 will redirect to that specific route. As no such routes are already defined, it renders error.html
We need to add some redirection rules under static web hosting. So, when 404 occurs, S3 needs to redirect to index.html, but this can be done only by adding some prefix like #!. Now, when you reload the page with any react route, instead of going to error.html, the same url will be loaded but with a prefix #!. Click Edit redirection rules and add the following code:
<RoutingRules>
<RoutingRule>
<Condition>
<HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<HostName> [YOUR_ENDPOINT] </HostName>
<ReplaceKeyPrefixWith>#!/</ReplaceKeyPrefixWith>
</Redirect>
</RoutingRule>
</RoutingRules>
In react, we need to remove that prefix and push the route to original route. Here is the change you need to do in react app. I have my main.js file, which is the starting point of react app. Add a listener to browserHistory like this:
browserHistory.listen(location => {
const path = (/#!(\/.*)$/.exec(location.hash) || [])[1];
if (path) {
history.replace(path);
}
});
The above code will remove the prefix and push the history to the correct route (HTML 5 browser history). For eg: something like this http://s3.hosting/#!/event will change to http://s3.hosting/event. Doing that makes react-router to understand and change the route accordingly. Here is my main.js file for better understanding:
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, compose, applyMiddleware} from 'redux';
import {Provider} from 'react-redux'
import {Router, Route, IndexRoute, browserHistory, useRouterHistory} from 'react-router';
import {syncHistoryWithStore} from 'react-router-redux';
import createLogger from 'redux-logger';
import thunk from 'redux-thunk';
const logger = createLogger();
const createStoreWithMiddleware = applyMiddleware(thunk, logger)(createStore);
const store = createStoreWithMiddleware(reducers);
const history = syncHistoryWithStore(browserHistory, store);
browserHistory.listen(location => {
const path = (/#!(\/.*)$/.exec(location.hash) || [])[1];
if (path) {
history.replace(path);
}
});
So uploading the browserified or webpacked react app(app.js file) will do the required need. Since I found difficult to gather all the stuff, I have aggregated all these as steps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With