circuit

How to Deploy React App with S3 and CloudFront (Updated for 2021)




In this blog post, we will deploy a React App to AWS S3 and Cloudfront. The React app is created using the create-react-app boilerplate and uses a dynamic routing with react-router-dom package.

In the last years S3 policy has changed a little bit, AWS introduced a block all public config as default so I will show how you can keep your S3 Private and serve the React App using Cloudfront so you will have both HTTPS by default and you will be using edge location of Cloudfront to speed up the website hosting.

If you prefer watching a video tutorial, you can find it here:

Deploy React App Video

Create React App

Firstly, let’s create the app using npx create-react-app my-app. This command will bootstrap a new React App with a solid structure so we can start playing with it. After the creation, we need to add the basic routing on the main file. We will use the react-router-dom package and copy the basic example from their website.

The final App.js will look like this:

import logo from './logo.svg';
import './App.css';
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}

function Dashboard() {
  return <h2>This is the Dashboard</h2>;
}



function App() {
  return (
    <div className="App">
      <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
            <li>
              <Link to="/users">Users</Link>
            </li>
            <li>
              <Link to="/dashboard">Dashboard</Link>
            </li>
          </ul>
        </nav>

        {/* A <Switch> looks through its children <Route>s and
            renders the first one that matches the current URL. */}
        <Switch>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/users">
            <Users />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
          <Route path="/">
            <Home />
          </Route>

        </Switch>
      </div>
    </Router>
    </div>
  );
}

export default App;

Now, if we run npm run start , the App will show a simple navigation menu where we can navigate through the different pages that we have just created. Our App is ready to be deployed.

Create S3 Bucket

Amazon’s S3 buckets are a popular option for storing static assets. One of the most common use cases of S3 is storing images for display on a web or mobile app.

  • Log into your AWS Account

  • Navigate to the S3 service and click Create Bucket. Make up a name for your new bucket, I will use my-example-app-deployment.

  • In the Set Permissions step, leave all the boxes checked. Your S3 bucket will remain private and the website will be served using Cloudfront.

  • You don’t need to enable the “Static Website hosting”, indeed the S3 bucket will be used only to host the files and not the website itself.

  • Click Create Bucket.

Create AWS CloudFront Distribution

The next step is to create the AWS Cloudfront distribution. CloudFront is the Content Delivery Network (CDN) service offered by AWS. CDNs optimize for speed by pushing your content out to edge locations, making it highly available around the globe.

  • Go to the CloudFront service in the AWS console, click Create Distribution and choose Web Distribution.

  • Select your Origin Settings. The Origin Domain Name choices pre-populate with S3 buckets. Selecting yours will also populate the Origin ID.

  • Note that we are restricting bucket access as we want our S3 Bucket to be private. To allow CloudFront access to the bucket, we create a new identity and tell CloudFront to update the Bucket Policy.

  • Select Create Distribution.

  • The next step is to select the created Distribution then click on the Error Pages tab. Select Create Custom Error Response.

  • Select Yes for a custom error response, set /index.html for the response page path and 200: OK for the response code. When done, click Create. We will do this process for errors 403 and 404.

Great! Now we have to wait a few minutes for the Distribution to be up and running.

Bonus: 307 Redirect CloudFront Error

Since our application is accessed by CloudFront distribution and our S3 Bucket is private, you could experience an annoying Access Denied error. This is due to the internal DNS propagation of the S3 bucket name:

You can find a full explanation in this AWS Forum Thread.

Use AWS CLI to update S3 and CloudFront

You can automate the deployment process with the AWS Command Line Interface. Ideally, you add an npm script to run build the application and push it to your S3 bucket. Briefly:

  • Install the AWS CLI.

  • Configure the CLI with aws configure. For example:

    AWS Access Key ID [None]:
    AWS Secret Access Key [None]:
    Default region name [None]: eu-west-1
    Default output format [None]: json
    

The AWS CLI has a command called sync to sync the content of a local folder to the S3 Bucket. Running npm run build will create a build folder that the script will upload to the S3 bucket. For example:

  "client-s3-deploy":"aws s3 sync build/ s3://{your-s3-bucket}"

Since our CloudFront distribution caches the content of the S3 Bucket and doesn’t know if the content has been updated, the application needs to tell CloudFront Distribution to invalidate the cache. We can do that from the AWS CLI, adding the following script:

  "client-cloudfron-invalidation":"aws cloudfront create-invalidation --distribution-id YOUR_DISTRIBUTION_ID --paths '/*'"

This script tells CloudFront to invalidate the cache for every item (note the --path '/*') .

Conclusion

In this blog, we have seen how we can deploy a React App using S3 and CloudFront maintaining the S3 Bucket private. Using Cloudfront is also possible to add a CNAME so your users will navigate to your app using a custom domain name.

Follow me on Twitter and Youtube for more!




Continue Learning