You've used React to build a web app. Now you want to share it with the world.
While there are seemingly infinite options for file hosting, it's hard to go wrong with AWS' S3 (Simple Storage Service):
- AWS' free tier has reasonably high limits for S3. Beyond that, S3 is cheap.
- S3 is fast and you can choose from data centers across the globe.
- Should you decide to add an API server for your React app to talk to, AWS is the gold standard of cloud platforms.
All this means you can quickly upload your work for friends or clients to see and not pay a dime.
In this post, we'll cover:
- What buckets and objects are in S3
- How to setup a bucket for hosting a React app
- How to bundle and upload a React app built with create-react-app to S3
-
How to setup
s3cmd
to make future deployments easier
Update 3/22/2016: Mere moments after this post went live, the AWS console received a facelift 🤦♂️
The screenshots might not match the console interface you're seeing, but the overall workflow is the same.
The app
We'll work with a simple app in this post. It's a hero generator. For heroes like you:
You can check out the code over on GitHub. The app was created with create-react-app.
If you want to follow along at home, just clone the repo:
$ git clone [email protected]:fullstackreact/hero-generator
We've only made small additions to an app generated with create-react-app. Specifically, we've added some styles and two components:
App
: the meat and potatoes-
HiddenImages
: A little trick to have the browser prefetch all the hero images so that switching between them is speedy
Setting up S3
First, if you don't have an account already, go create an AWS account here: https://aws.amazon.com/.
After divulging all your personal info to Amazon and answering a phone call from one of their friendly robots, visit the AWS console: https://console.aws.amazon.com. You might be prompted to sign in again.
After reaching the console, click the "Services" button in the top left:
This will reveal the largest drop-down you've ever seen:
Locate and click on S3. That will bring you to the page https://console.aws.amazon.com/s3:
S3 buckets and objects
In S3, a bucket is a collection of objects. A bucket is a virtual container. Objects are files belonging to that container.
While there are ways to configure AWS to serve multiple websites out of a single bucket, it's easier to have a 1-to-1 relationship between buckets and sites. So, for our purposes, we'll want to create a new bucket every time we want to deploy a new React app.
Click on the "Create Bucket" button. You'll be asked to specify a bucket name and a region:
Bucket regions
You create a bucket inside a specific AWS region. AWS has datacenters around the world:
In AWS, different regions can have different prices. Our React app is less than 30MB though, so price differences won't matter. Instead, it's generally good practice to locate your bucket closest to you or your users.
AWS has a free tier for the first 12 months. Check out the details here. The limits are quite reasonable.
Bucket names
As for the bucket name, there are two things to consider:
- Bucket names must be globally unique across AWS
- If you are using a domain, you should name your bucket after your domain
As for #1, I wanted to demonstrate this by showing a screenshot of me attempting to use a bucket name that would be obviously already taken. But I guess Jeff Bezos is a busy guy and doesn't have time to be going around creating buckets? So I have his namesake bucket now...
As for #2, we won't worry about this too much in this post. Instead, I'll direct you to an AWS article on the topic at the end. Just use a generic bucket name for the time being. (If you try to come up with a cool domain name right now, you will never finish this post.)
Setting up your bucket
We have our bucket. In my case, it's
bezos
over in Montreal:
We need to do three things:
- Enable static website hosting. This is why we're here, right??
- Set up permissions. Our bucket is locked down right now. In order for our bucket to be an effective host for our website, we need to tell AWS that we're OK with people requesting objects from the bucket.
- Upload our React app to the bucket.
1. Enable static website hosting
Ensure your bucket is highlighted so you can see the
properties on the right-hand side. Click "Static Website
Hosting" and then "Enable website hosting." Our
index document will eventually be index.html
, so
fill in that value and click "Save":
2. Set up permissions
In S3, buckets can have different policies. The policy specifies who can do what to which objects in a given bucket.
In the case of you and your React app, you want to setup your bucket such that you are the only one that can write to it. However, you want the world to be able to view it.
To enable the world to view objects in your bucket, we'll add a bucket policy. Click on "Permissions" then "Add bucket policy":
Policies are represented as JSON documents. We'll use the
following policy. Copy and paste it into the bucket policy
prompt. Just change the placeholder
<BUCKET-NAME>
to your bucket's name:
{
"Version": "2012-10-17",
"Statement":[
{
"Sid":"AddPerm",
"Effect":"Allow",
"Principal": "*",
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::<BUCKET-NAME>/*"]
}
]
}
Breaking this down:
-
Principal
: This is the who. Using a*
, we're saying everyone. Literally, everyone. -
Action
: This is what the principal can do. We're saying everyone can perform a "get." -
Resource
: This is the which. Against which resources (objects) can the principal perform this action?
So, all together, we're saying everyone can get any object in <BUCKET-NAME>.
You might be tempted to edit the
Version
field. Don't. TheVersion
field is not the version of your policy but the version of AWS' policy grammar. You can read more here, but basically as of the time of this writing the value for this field will always be2012-10-17
for new policies.
Here's the bucket policy for bezos
:
Click "Save." The masses can now access our (empty) bucket!
In the future, you can always use AWS' bucket policy examples page or their policy generator should you need to create a nuanced policy.
3. Upload our React app to the bucket
We now have a bucket living in the cloud somewhere (supposedly Montreal). We've told AWS we want to use it for static website hosting. And we've set things up so that the world can access all the objects in our bucket. Now, we just need to upload our website.
First, we need the static website to upload.
In our example, we're using
create-react-app
. We can run the
npm run build
command which will create a
build/
folder. That folder will be totally
self-contained. Everything needed to run our app will be
included.
Let's run that command now:
Taking a look inside build/
:
$ ls -1p build/
asset-manifest.json
assets/
favicon.ico
index.html
static/
Under assets/
is our images folder. Inside
static/
is minified versions of our CSS and JS,
including all our app's components and the entire React
library.
index.html
is the centerpiece of our app. If you
were to look at the file, you'd see that it loads all of
our CSS and JavaScript files.
Remember how we instructed AWS that we'd like the index
document of our static website to be index.html
?
That will be this document right here.
We just need to upload the contents of build/
to
S3.
Over on the S3 console, we'll enter the bucket by clicking on its name. Then we'll click on "Upload":
Now, we want to upload the contents of build/
to
S3. Here's an important nuance: If we upload the
build
folder, things won't work. S3
is expecting the index.html
file at the top-level
of our bucket. So we need to drag and drop the
contents of the build
folder, like so:
Note that the upload size is 29MB. 14 of these MB is the images. If you're on a slow connection, you can consider not uploading all the images.
After clicking "Start Upload" and waiting a bit (apologies to the other patrons currently at my coffeeshop), we'll see all of our files listed in the bucket:
Our website is up in the cloud. And the world has access to it.
But ... how do we see it?
To find out, click on the index.html
file. And
then ensure that the right-hand view is toggled to
"Properties." The full URL to this file will be
listed:
Don't use the first link listed, which is this format:
// wrong URL
<bucket-region>.amazonaws.com/<bucket-name>/index.html
Instead, you're looking for this format:
// million-dollar URL
<bucket-name>.s3-website.<bucket-region>.amazonaws.com
Here's our running web app, sitting on magnetic plates somewhere in Montreal but accessible to the world:
Setting up s3cmd
Dragging and dropping your website to S3 every time you make changes certainly works, but won't win you many points at cocktail parties.
We can use a command line tool, s3cmd
, to impress
our friends and neighbors.
AWS also provides its own CLI, which you can find instructions for installing here.
AWS' CLI operates on much more than just S3 but requires a little bit more work to setup. If you want to give it a shot, check out these setup instructions.
Get the tool here: http://s3tools.org/s3cmd. Or, if you're on OSX and have homebrew installed, you can do this:
$ brew install s3cmd
In order to use s3cmd
, we need our
access keys from AWS. Remember, our bucket is
read-only to the world. In order to make
changes to it, we have to authenticate ourselves as the owner.
To get your access keys, follow this AWS tutorial. I'll be here when you get back.
With your keys in hand, you can run this command to have s3cmd use these keys indefinitely:
s3cmd --configure
s3cmd
will prompt you for your keys. Copy and
paste your access key and secret key from the AWS console.
If you want to skip configuration for now, you can just use your keys during subsequent commands like this:
s3cmd --access_key=<ACCESS-KEY> --secret_key=<SECRET-KEY> <COMMAND>
With s3cmd
configured, we can verify it works by
asking the tool to list all our buckets:
$ s3cmd ls
2017-03-10 17:11 s3://bezos
Cool! Now, we can use this handy command to "sync" the contents of our build folder with this bucket:
$ s3cmd sync LOCAL_DIR s3://BUCKET[/PREFIX]
In my case, that'll look like this:
$ s3cmd sync build/* s3://bezos
Any time I make changes to my React app in the future that I want to deploy, I just need to run the following commands in sequence:
$ npm run build
$ s3cmd sync build/* s3://bezos
Naturally, we can add a command to our
package.json
that does this for us:
// in package.json
"build-and-deploy": "npm run build && s3cmd sync build/* s3://bezos && echo '🚀 Deployed!'"
What's next?
I mentioned earlier that if you finished the post, I'd remind you about that article on AWS about using custom domains. So, reminded.
Oh, and go build something useful. ❤️🏔
Found this post helpful? Then you'll love our book — it's packed with over 800 pages of content and over a dozen projects, including chapters on React fundamentals, Redux, Relay, GraphQL, and more.