Deploy a React App to Amazon S3

Deploy a React frontend to AWS S3 and configure it as a public static website.

Introduction

30 Second Summary

Every website you visit is stored on a server somewhere in the world. Knowing how to put your own projects on that server is one of the most in-demand skills in tech.

In this project, you will deploy your Hope AI React frontend to Amazon S3 as a static website. The end result is a live public URL where anyone can access your app.

What You'll Build

You will host your existing Hope AI React app on an S3 bucket configured for static website hosting.

By the end of this project, you'll have:

  • A production build of your Hope AI app, optimized and ready for the cloud.
  • An S3 bucket configured for static website hosting with a public endpoint.
  • A bucket policy granting public read access to your site files.
  • Secret Mission: Configure a custom 404 error page that matches your app's design.

Are there any prerequisites?

You need an AWS account, Node.js and npm installed, and your existing Hope AI React + Vite frontend project. If you've completed the earlier Hope AI projects, you're ready to go.

Build Your React App for Production

Your Hope AI app currently runs in development mode on your local machine. To host it on AWS S3, you need production-ready files that any web server can serve without a Node.js runtime.

Vite includes a build command that bundles your React components into plain HTML, CSS, and JavaScript. In this step, you'll generate that production build and verify it works before uploading anything to AWS.

In this step, get ready to:
  • Run the Vite build command to generate production files.
  • Verify the build works by previewing it locally.
Run the production build

A production build takes all your React source files and compiles them into optimized, minified static assets. The output is a dist folder containing everything a web server needs to serve your app.

  • In VS Code, open the integrated terminal by pressing Ctrl+` (macOS) or Ctrl+` (Windows).
  • Generate your production build by running this command:
npm run build

What does this command do?

npm run build triggers Vite's build process. Vite takes your React components, bundles them together, minifies the code, and outputs everything into a dist folder.

The result is plain HTML, CSS, and JavaScript that any static file server (like S3) can serve directly. No Node.js runtime needed on the server.

  • Confirm the build output exists by running:
ls dist

You should see index.html and an assets folder listed in the output.

Build failed or no dist folder?

  • Make sure your terminal is in the project root folder (where your package.json lives). Run ls to check you can see package.json in the output.
  • If you see a syntax error, check that your React source files have no typos. The error message will point to the exact file and line.
  • If you see a missing dependency error, run npm install first to make sure all packages are installed.

Help me debug my Vite build error

Preview your build locally

Before uploading to S3, it's worth confirming your production build actually works. Vite includes a preview server that serves your dist folder exactly like a static web server would.

  • Start the local preview server by running this command:
npx vite preview
  • Open http://localhost:4173 in your browser.
  • Confirm your Hope AI app loads and works correctly. Try sending a request to verify the frontend communicates with your backend.
  • Once verified, go back to your terminal and press Ctrl+C to stop the preview server.

Why preview before uploading?

The preview server mimics how a static hosting service (like S3) would serve your files. If something looks broken here, it will also be broken on S3. Catching issues locally saves you the upload-test-fix cycle.

Page is blank or app won't load?

  • Make sure you're visiting http://localhost:4173 (not the usual localhost:5173 dev server port).
  • Check your terminal for errors. If you see a message about a missing file, try running the build again.
  • If the app loads but API calls fail, that's OK for now. Your backend on Render may need the S3 URL added to its CORS configuration later.

Help me fix my Vite preview

Your production build is ready to go. Next up, you'll create an S3 bucket on AWS to host these files and make your app publicly accessible.

Create an S3 Bucket

Your production build is ready. The dist folder on your machine contains everything your app needs to run in a browser.

Now you need somewhere in the cloud to host those files so anyone can access your app via a URL. Amazon S3 is AWS's object storage service, and it can serve static files directly to browsers.

In this step, get ready to:
  • Sign in to the AWS Management Console and open the S3 service.
  • Create a new S3 bucket to store your website files.
Open the S3 console

The AWS Management Console is a web-based interface for managing all AWS services. You will navigate to the S3 service within it.

  • Open your browser and go to https://console.aws.amazon.com/s3/.
  • Sign in with your AWS account credentials if prompted.
  • In the left navigation pane, choose General purpose buckets.
  • Choose Create bucket.

What is an S3 bucket?

A bucket is a container for storing objects (files) in Amazon S3. Every file you upload to S3 lives inside a bucket.

Think of it like a top-level folder in the cloud. Your bucket will hold all the HTML, CSS, and JavaScript files that make up your Hope AI frontend.

Configure and create your bucket

Bucket names must be globally unique across all AWS accounts. You cannot change the name after creation, so choose carefully.

  • In the Bucket name field, enter a unique name. For example, hope-ai-frontend. If that name is taken, try adding your initials or a number (e.g., hope-ai-frontend-jd).
  • In the navigation bar at the top of the page, choose a Region close to you. For example, if you are in India, choose Asia Pacific (Mumbai).
  • Leave all other settings at their defaults.
  • Scroll to the bottom and choose Create bucket.

Why does the bucket name need to be globally unique?

S3 bucket names share a global namespace across all AWS customers. No two buckets anywhere in the world can have the same name. This is because the bucket name becomes part of your website's public URL.

After creating the bucket, you are taken back to the bucket list. Your new bucket should appear in the list.

Don't see your bucket in the list?

Make sure you are viewing General purpose buckets in the left navigation pane. If you see a "Bucket name already exists" error, the name is taken by another AWS account. Try a different name with your initials or a number appended.

Help me troubleshoot my S3 bucket creation.

Your bucket is created and ready for the next step. Next up, you will configure it to serve your files as a website.

Configure Static Website Hosting

Your S3 bucket is created and ready to hold files. Right now, though, it's just a storage container with no way to serve web pages to visitors.

S3 has a built-in static website hosting feature that turns your bucket into a simple web server. Once enabled, AWS gives you a public endpoint URL where anyone can access your site.

In this step, get ready to:
  • Enable static website hosting on your S3 bucket.
  • Note the public endpoint URL where your site will be accessible.
Enable static website hosting

By default, S3 buckets only store objects. You need to explicitly tell AWS that this bucket should behave like a web server and serve files to browsers.

  • In the S3 console, choose your bucket name (hope-ai-frontend) from the bucket list.
  • Choose the Properties tab.
  • Scroll down to the Static website hosting section and choose Edit.
  • Select Enable.
  • Choose Host a static website.
  • In the Index document field, enter index.html.
  • Leave the Error document field empty for now.
  • Choose Save changes.

What is the index document?

The index document is the default page S3 serves when someone visits your website URL. It works just like how a web server serves index.html when you navigate to a site's root address.

The name is case-sensitive and must exactly match the file you upload. Since Vite's build outputs index.html (all lowercase), that's what you enter here.

Don't see Static website hosting?

Make sure you're on the Properties tab (not Permissions or Objects). The Static website hosting section is near the bottom of the page, so scroll all the way down.

Help me find the Static website hosting settings in my S3 bucket.

Note your bucket website endpoint URL

After saving, AWS generates a public URL for your website. This is the address people will use to visit your deployed app.

  • Stay on the Properties tab and scroll back down to the Static website hosting section.
  • Find the Bucket website endpoint URL displayed there.
  • Copy this URL and save it somewhere (a text file or sticky note). You will need it in a later step.

What does my endpoint URL look like?

Your endpoint follows this format: http://your-bucket-name.s3-website-region.amazonaws.com.

For example, if your bucket is named hope-ai-frontend in the Mumbai region, it would be http://hope-ai-frontend.s3-website.ap-south-1.amazonaws.com. This URL won't work yet because we haven't configured public access permissions.

Don't see an endpoint URL?

If the Static website hosting section still shows "Disabled", your changes may not have saved. Choose Edit again, confirm Enable is selected and index.html is in the Index document field, then choose Save changes.

Help me enable static website hosting on my S3 bucket.

Your bucket now knows how to serve a website. Next up, you'll configure permissions so the rest of the world can actually access it.

Set Permissions for Public Access

Your bucket has static website hosting enabled, but nobody can actually visit the site yet. By default, S3 blocks all public access to protect your data.

In this step, you'll unlock public access and add a bucket policy that tells AWS exactly who can read your files. This is your first hands-on encounter with cloud permissions.

In this step, get ready to:
  • Disable the Block public access setting on your bucket.
  • Add a bucket policy granting public read access to your website files.
Disable Block public access

Amazon S3 blocks public access by default. This is a safety feature that prevents accidental data exposure. Since you want the world to view your website, you need to explicitly turn this off.

  • Choose the Permissions tab for your bucket.
  • Under Block public access (bucket settings), choose Edit.
  • Clear the checkbox for Block all public access.
  • Choose Save changes.
  • Type confirm in the confirmation dialog, then choose Confirm.

Why does AWS make you type confirm?

Disabling public access protection is a significant security decision. AWS adds this extra confirmation step to prevent accidental exposure of private data.

For a static website, public read access is exactly what you want. Visitors need to download your HTML, CSS, and JavaScript files to view the site.

After saving, you should see Objects can be public displayed under the Block public access section. This confirms the setting took effect.

Don't see Objects can be public?

Make sure you cleared the top-level checkbox labeled Block all public access. Clearing only the sub-checkboxes is not enough.

Check that you typed confirm exactly (lowercase, no spaces) in the confirmation dialog.

Help me troubleshoot Block public access settings in S3.

Add a bucket policy for public read access

Disabling Block public access removes the guardrail, but it doesn't grant anyone permission yet. A bucket policy is a JSON document that defines exactly who can do what with your bucket's objects.

  • Still on the Permissions tab, scroll down to Bucket policy.
  • Choose Edit.
  • Paste the following policy into the editor, replacing the bucket name with your actual bucket name:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::[[BUCKET_NAME="your-bucket-name"]]/*"
    }
  ]
}

What does this policy do?

  • "Principal": "*" means anyone on the internet.
  • "Action": "s3:GetObject" grants permission to read (download) objects. It does not allow writing, deleting, or listing.
  • "Resource" with /* at the end means every object inside the bucket.

This is the minimum permission needed to serve a static website. Visitors can view your pages but cannot modify or delete anything.

  • Choose Save changes.
  • Verify that the Bucket policy section now displays your policy JSON.

Seeing an Access Denied error when saving?

Make sure you completed the previous substep first. If Block public access is still enabled, AWS rejects any policy that grants public access.

Double check that the bucket name in your Resource line matches your actual bucket name exactly. A single typo will cause an error.

Help me fix my S3 bucket policy.

Your bucket is now open for business. Anyone with the website endpoint URL can read your files. Next up, you'll upload your production build and see your app go live.

Upload Files and Go Live

Your bucket is fully configured with the right permissions to serve content publicly. The last step is getting your actual files into the bucket so the world can see your app.

In this step, you'll upload your production build files to S3 and verify that your Hope AI frontend is live and accessible at its public endpoint URL.

In this step, get ready to:
  • Upload your production build files to your S3 bucket.
  • Verify your app is live and functional at the S3 website endpoint.
Upload your build files to S3

Your local dist folder contains the production-ready version of your app. You need to upload its contents directly into the bucket root.

  • In the S3 console, choose your hope-ai-frontend bucket from the bucket list.
  • Choose the Objects tab.
  • Choose Upload.
  • Choose Add files and select the index.html file from inside your local dist folder.
  • Choose Add folder and select the assets folder from inside your local dist folder.
  • Choose Upload.

Upload the contents of dist, not the dist folder itself

Make sure you are selecting files and folders from inside the dist folder. If you upload the dist folder itself, your files will be nested under a dist/ prefix and the website endpoint will not find index.html at the bucket root.

What should I see?

After the upload completes, you see a success message on the Upload status page. Choose Close to return to the Objects tab.

You should see index.html listed as an object and an assets/ folder containing your bundled JavaScript and CSS files.

Verify your app is live

Your files are now in the bucket. Time to visit the public URL and confirm everything works.

  • Switch to the Properties tab for your bucket.
  • Scroll down to the Static website hosting section.
  • Click the Bucket website endpoint URL to open it in a new browser tab.

Your Hope AI frontend should load and be fully functional. Try sending a message to confirm it communicates with your FastAPI backend on Render.

What is the endpoint URL format?

Your S3 website endpoint follows the format http://your-bucket-name.s3-website-region.amazonaws.com. This is an HTTP endpoint provided by S3 for static website hosting.

Seeing a 403 Forbidden error?

This means your bucket policy or public access settings are not configured correctly. Go back to the Permissions tab and confirm that Block public access is disabled and the bucket policy is saved.

If you see a 404 Not Found error instead, you likely uploaded the dist folder itself rather than its contents. Check the Objects tab. You should see index.html at the top level, not nested inside a dist/ prefix.

help me debug why my S3 static website is returning an error

Secret mission

Add a Custom 404 Error Page

Right now, if someone visits a broken link on your site, they see a generic XML error from AWS. In this secret mission, you'll create a custom error page that matches your app's style and configure S3 to serve it automatically.

Clean Up Your Resources

Clean Up Your Resources

This project uses the AWS Free Tier. S3 Standard storage includes 5 GB and 20,000 GET requests per month at no cost. Your React app build is typically a few MB, so you will not come close to any limits.

Decide whether to keep your resources running or delete them entirely.

Resources you used:

  • S3 bucket (hope-ai-frontend) with your website files.
  • Local dist folder (production build output).

Keep everything running

No action needed. Choose this if you are still actively building or want to keep your site live.

  • Your site stays accessible at the S3 website endpoint URL at no cost within Free Tier limits.
  • Your local dist folder stays in your project for future re-uploads.

Pause - I'll come back to this later

Since this project runs entirely within the AWS Free Tier, there is nothing to pause for cost reasons. Your site will remain available at no charge.

  • If you want to take your site offline temporarily, you can empty the bucket objects without deleting the bucket. This preserves your hosting configuration so you can re-upload later without reconfiguring.
  • Your local dist folder remains on your machine. You can regenerate it anytime with npm run build.

Delete - I don't want to use this again

Remove all project resources and start fresh. Follow these steps to delete your S3 bucket and clean up local build files.

Delete the S3 bucket:

  • Open the S3 console at https://console.aws.amazon.com/s3/.
  • In the left navigation pane, choose General purpose buckets.
  • Select the option button next to hope-ai-frontend, then choose Empty.
  • Type permanently delete in the confirmation field, then choose Empty.
  • Return to the bucket list. Select hope-ai-frontend again, then choose Delete.
  • Type the bucket name (hope-ai-frontend) to confirm, then choose Delete bucket.

Delete local build files:

  • In your terminal, navigate to your Hope AI project folder and remove the dist folder by running:
rm -rf dist
  • On Windows, run:
rmdir /s /q dist
  • Optionally, delete the public/404.html file if you no longer want the custom error page in your project source.

That's a Wrap!

That's a Wrap!

Nice work! You just deployed a real application to AWS cloud infrastructure. Your Hope AI frontend is live on S3, accessible to anyone with the public URL.

You've learned how to:

  • Built a production React app with Vite and deployed it to AWS S3 as a live static website.
  • Created and configured an S3 bucket for static website hosting with a public endpoint.
  • Wrote a bucket policy granting public read access, a core IAM and permissions skill.
  • Secret Mission: Configured a custom 404 error page for production-quality error handling.

Ready to quiz yourself?