Tuesday, November 29, 2022
HomeSoftware EngineeringHow to Deploy React App to S3 and CloudFront

How to Deploy React App to S3 and CloudFront

[ad_1]

If you would like to deploy a React App to AWS S3 and AWS CloudFront, then you can follow this guide.

The following solution creates a React App and deploys it to S3 and CloudFront using the client’s CLI.
It also chains commands so that a React build, S3 sync and CloudFront invalidation can occur with a single command.

Code available at GitHub

https://github.com/ao/deploy-react-to-s3-cloudfront

Target Architecture

Create a directory for the application:

mkdir deploy_react && cd $_

Code language: Bash (bash)

Create the React App using create-react-app from npx:

npx create-react-app sample-react-app

Code language: Bash (bash)

(Optional) Open the project in VS Code:

code .

Code language: Bash (bash)

Change directory and run the app:

cd sample-react-app<br>npm start

Code language: Bash (bash)

Now we need to install react-router-dom so that we can change routes between pages in our React app.

npm i react-router-dom

Code language: Bash (bash)

Once this is done, we can edit our code before moving onto the deployment steps.

Open the App.js file under the src directory and replace all the code in the file with the following:

import './App.css'; import React from "react"; import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom"; const Home = () => { return <h2>Home</h2> } const About = () => { return <h2>About</h2> } function App() { return ( <div className="App"> <Router> <div> <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/about">About</Link> </li> </ul> </nav> <div className="content"> <Routes> <Route path="/about" element={<About />} /> <Route path="/" element={<Home />} /> </Routes> </div> </div> </Router> </div> ); } export default App;

Code language: JavaScript (javascript)

Open the App.css file as replace it with the following:

ul { padding: ; } li { display:inline; padding: 10px; } .content { padding: 10px; }

Code language: CSS (css)

If we run the React app with npm start, we will now see the following:

If we click on About in the navigation, the page changes and shows the About component.

Head over to the S3 console and create a new bucket.
Give it a unique bucket name and click Create bucket.

We now have a new bucket, with nothing inside.

Head over to CloudFront and create a distribution:

Select the Origin domain, which will be the newly created S3 bucket.
Specify a Name. Note that it will create one for you from the Origin domain by default if you don’t specify one yourself.

For S3 bucket access, Choose Yes use OAI, create a new OAI and select Yes for the Bucket policy Update.

Under Default cache behavior, select Redirect HTTP to HTTPS.

Under Settings, specify the Default root object to be index.html

Leave all other fields as is and click Create distribution.

You will now see a distribution being created for you.

Note that this will take a couple of minutes to get ready,

In the package.json file, under src/, locate the following scripts lines:

"scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" },

Code language: JSON / JSON with Comments (json)

Here we will add some more options:
We will add a new script called deploy-to-s3 and it will run the following command:
aws s3 sync build/ s3://<your_s3_bucket_name>

Note that you can also specify an AWS_PROFILE here as follows if needed:
aws s3 sync build/ s3://<your_s3_bucket_name> --profile <profile_name>

Update the scripts section to look as below, but change your own S3 bucket name inplace:

"scripts": { "start": "react-scripts start", "build": "react-scripts build", "deploy-to-s3": "aws s3 sync build/ s3://sample-react-app-123654789", "test": "react-scripts test", "eject": "react-scripts eject" },

Code language: JSON / JSON with Comments (json)

Now we need to create a build of our React app, so that we can push it’s contents to S3.
To do this, run the following command:
npm run build

Then deploy it to S3 as follows:
npm run deploy-to-s3

Now if we look in the S3 console, we can see the files that were deloyed:

We now need to setup the CloudFront pages, which we will do through the CloudFront console.

Under the CloudFront distribution, click Create custom error response.
We do this because React is a Single Page Application (SPA) and no physical files exist on the server for the different Routes that we have specified. They are all dynamic.
For example, /about does not exist as a logical path on the drive, or server. So instead, it will be a 404 Not Foundwhen called upon. So therefore, we will tell CloudFront that for all 404 Not Found paths, we want index.html to handle them.
Remember that index.html is the path for where React initializes.

To this end, create a 404 Not Found custom error response, that points to our /index.html file, with a status of 200 OK:

Also create a 403 Forbidden custom error response, that points to our /index.html file, with a status of 200 OK:

Once both have been created, the Error pages should have two (2) entries as follows:

If we don’t create these, then we will get the AccessDenied error when trying to access any of the Routes we specified in the React app, which look like this:

Now instead, we can see the actual Route itself:

Everytime we update the CloudFront distribution, by deploying new files to S3, we need to Invalidate the files.

Head over to the package.json file from before and add another command under the one we just added:
It will look something like this:

aws cloudfront create-invalidation --distribution-id <distribution_id> --paths '/*' --profile <profile_name>

Code language: Bash (bash)

You don’t need to specify the --profile argument, unless you need to.

We can get the Distribution ID from CloudFront itself:

Update this new section as follows, remember to replace your --distribution-id:

"scripts": { "start": "react-scripts start", "build": "react-scripts build", "deploy-to-s3": "aws s3 sync build/ s3://sample-react-app-123654789", "invalidate-cloudfront": "aws cloudfront create-invalidation --distribution-id EIAUK8JFBCT6S -- paths '/*'", "test": "react-scripts test", "eject": "react-scripts eject" },

Code language: JSON / JSON with Comments (json)

If you run that step alone, you will get a verification as follows:

{ "Location": "https://cloudfront.amazonaws.com/2020-05-31/distribution/EIAUK8JFBCT6S/invalidation/I17X51041BLJHR", "Invalidation": { "Id": "I17X51041BLJHR", "Status": "InProgress", "CreateTime": "2022-08-17T18:16:56.890000+00:00", "InvalidationBatch": { "Paths": { "Quantity": 1, "Items": [ "/*" ] }, "CallerReference": "cli-1660760215-662979" } } }

Code language: JSON / JSON with Comments (json)

Now that we have both the steps we need, let’s create an aggregate command that will tie everything together, so that we only need to run a single command each time:

We will add the following script:

"deploy": "npm run build && npm run deploy-to-s3 && npm run invalidate-cloudfront",

Code language: JSON / JSON with Comments (json)

So once we have added it to the scripts block, it will all look like this:

"scripts": { "start": "react-scripts start", "build": "react-scripts build", "deploy-to-s3": "aws s3 sync build/ s3://sample-react-app-123654789", "invalidate-cloudfront": "aws cloudfront create-invalidation --distribution-id EIAUK8JFBCT6S --paths '/*'", "deploy": "npm run build && npm run deploy-to-s3 && npm run invalidate-cloudfront", "test": "react-scripts test", "eject": "react-scripts eject" },

Code language: JSON / JSON with Comments (json)

This now means we have a single command to build our React App, sync the files to S3, and invalidate the files in CloudFront, as a chained command.

If we take the current state of the deployed application on CloudFront, it looks like this:

If we open the App.js file and create a new Route:

<Route path="/testing" element={<Testing />} />

Code language: HTML, XML (xml)

Which is added as follows:

<div className="content"> <Routes> <Route path="/about" element={<About />} /> <Route path="/testing" element={<Testing />} /> <Route path="/" element={<Home />} /> </Routes> </div>

Code language: HTML, XML (xml)

Then add a new component for Testing:

const Testing = () => { return <h2>Testing</h2> }

Code language: JavaScript (javascript)

Then add a new nav item:

<li> <Link to="/testing">Testing</Link> </li>

Code language: HTML, XML (xml)

Now all we need to do to see the changes deployed, is run the following command:

npm run deploy

This will cycle through our steps and produce the following output:

> [email protected] deploy > npm run build && npm run deploy-to-s3 && npm run invalidate-cloudfront > [email protected] build > react-scripts build Creating an optimized production build... Compiled successfully. File sizes after gzip: 50.75 kB build/static/js/main.95dbd789.js 1.79 kB build/static/js/787.7c33f095.chunk.js 301 B build/static/css/main.58e1094f.css The project was built assuming it is hosted at /. You can control this with the homepage field in your package.json. The build folder is ready to be deployed. You may serve it with a static server: npm install -g serve serve -s build Find out more about deployment here: https://cra.link/deployment > [email protected] deploy-to-s3 > aws s3 sync build/ s3://sample-react-app-123654789 upload: build/asset-manifest.json to s3://sample-react-app-123654789/asset-manifest.json upload: build/static/js/787.7c33f095.chunk.js.map to s3://sample-react-app-123654789/static/js/787.7c33f095.chunk.js.map upload: build/index.html to s3://sample-react-app-123654789/index.html upload: build/robots.txt to s3://sample-react-app-123654789/robots.txt upload: build/manifest.json to s3://sample-react-app-123654789/manifest.json upload: build/static/js/787.7c33f095.chunk.js to s3://sample-react-app-123654789/static/js/787.7c33f095.chunk.js upload: build/favicon.ico to s3://sample-react-app-123654789/favicon.ico upload: build/static/css/main.58e1094f.css.map to s3://sample-react-app-123654789/static/css/main.58e1094f.css.map upload: build/static/css/main.58e1094f.css to s3://sample-react-app-123654789/static/css/main.58e1094f.css upload: build/logo512.png to s3://sample-react-app-123654789/logo512.png upload: build/logo192.png to s3://sample-react-app-123654789/logo192.png upload: build/static/js/main.95dbd789.js.LICENSE.txt to s3://sample-react-app-123654789/static/js/main.95dbd789.js.LICENSE.txt upload: build/static/js/main.95dbd789.js to s3://sample-react-app-123654789/static/js/main.95dbd789.js upload: build/static/js/main.95dbd789.js.map to s3://sample-react-app-123654789/static/js/main.95dbd789.js.map > [email protected] invalidate-cloudfront > aws cloudfront create-invalidation --distribution-id EIAUK8JFBCT6S --paths '/*'
Code language: plaintext (plaintext)

Now we can refresh the browser and we will see our new Route added and linked to our new TestingComponent as soon as the CloudFront invalidations have completed.

[ad_2]

Source link

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments