Deploying create-react-app to S3 and CloudFront

The new React CLI tool, create-react-app, is helping front end developers finally focus on building applications, rather than configuring them. Gone are the days of searching through the dozens of boilerplate and starter apps to get off the ground. For the modest price of accepting Facebook’s opinionated (but sensible) configuration settings, you’re off to the races.

Once you’ve kicked off a new application, npm run build will optimize, compile, and dump the static files required to serve your application in a build directory. At this point, you’re free to host the contents of that directory wherever you like — and voila, your app is on the web. Please note: if your application depends on a client-side server, such as Express, this article is sadly not for you.

Within the README of an app created with create-react-app, there are a number of helpful tips for how to deploy your application with a variety of tools: Github Pages, Heroku, Surge, and so on. We’ll walk through how to deploy to S3 and CloudFront on AWS.

Quickly — why deploy statically? I see two obvious wins:

  1. Ease. Deploying static files requires far fewer moving parts than an app with a server. There’s less to set up and less to maintain.
  2. Cost. Because there’s less to set up and maintain, the cost of deploying a static application can be dramatically cheaper.

Alrighty, lets hop to it.

Deploy to S3

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. There’s nothing stopping you from storing any static asset in an S3 bucket, though, so we’ll use it to store and display our HTML, CSS, JavaScript and any other assets required by our application.

  • Create an account or sign in to the AWS Console: https://aws.amazon.com/
  • Navigate to the S3 service and click Create Bucket. Make up a clever name for your new bucket, then click Create.
  • Click on the newly-created bucket. Within the Properties, open the Static Website Hosting tab, and select Enable website hosting. Fill in index.html for both the Index and Error Documents. By setting index.html as the Error Document, we can allow something like react-router to handle routes outside of the root.
  • Open the Permissions tab, then select Edit bucket policy. You may choose to do something more nuanced here, but a good starting point is to provide read-only permissions for anonymous users — a policy provided in the AWS examples. Make sure its your bucket name under the "Resource" key.
  • Add the contents of your build directory to this bucket. This can be done by clicking on the bucket and clicking Upload. That’s it! You can find the URL to your application back under the Static Website Hosting tab, labeled Endpoint.

Bonus: Deploying with AWS CLI

You can streamline the deployment process with the AWS Command Line Interface. For example, you might write an npm script to run tests, build the application and push to your S3 bucket. Briefly:

AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-west-2
Default output format [None]: json
  • Create a bucket and deploy the app:
// create a bucket
$ aws s3 mb s3://bucket-name
// list buckets
$ aws s3 ls
// build and deploy the app
$ npm run build && aws s3 sync build/ s3://bucket-name

The deploy script above is as simple as it gets. It takes the contents of the build directory (produced by npm run build) and replaces whatever is currently in your bucket with those contents. For a slightly more robust deployment pipeline, you may prefer to store the previous state of the application in another directory. You can then write a script to revert to a previous state, should you need to.

Other helpful CLI commands can be found here.

Note: at the time of writing, a bug exists that prevents Internet Explorer versions from utilizing the Error Document configuration with apps created with create-react-app. One way around this issue is to use CloudFront.

Deploy to CloundFront

If it works on S3, why bother with CloudFront?

CloudFront is the Content Delivery Network (CDN) service offered by AWS. CDNs optimize for speed by pushing your content out to edge locations around the world, making it highly available around the globe. If your users are only local, the performance offered by S3 may be just fine.

  • Select theCloudFront service in the AWS console, click Create Distribution, then under the web delivery method, click Get Started.
  • Select your Origin Settings. The Origin Domain Name choices pre-populate with S3 buckets. Selecting yours will also populate the Origin ID.
  • Within the Distribution Settings, set the Default Root Object to index.html.
  • You may choose to further configure the distribution, but that’s enough to get the job done. Select Create Distribution.
  • Click the ID of your newly created distribution to reach its settings page, 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. This custom error page in the CloudFront distribution is analogous to the Error Document on the S3 bucket (and will work on IE, too). When done, click Create.
  • That’s it! Give the deployment a handful of minutes, then check out your web app. You can find the URL on the distribution listings page, under the Domain Name column.

Wrapping Up

Easy enough, right? As mentioned, you can tweak countless other settings, configure CNAMEs in CloudFront or use Route 53 for DNS service. At any rate, the choices made within create-react-app suggest that static deployments are a preferred practice for React apps and here to stay. Do you prefer to configure anything differently? Let me know what and why in the comments.

Interested in a simple npm deploy script? This redux starter application (built with create-react-app) utilizes one here.