We are now ready to work on our frontend. So far we’ve built and deployed our backend API and infrastructure. We are now going to build a web app that connects to our backend.

We are going to create a single page app using React.js. We’ll use the Create React App project to set everything up.

Create a New React App

Run the following command in your project root.

$ npx create-react-app frontend --use-npm
$ cd frontend

This should take a second to run, and it will create your new project in the frontend/ directory.

Note that we are adding this inside our SST app. Create React App will throw a warning if it is installed inside a directory that uses Jest. And we were using Jest to run our tests. To disable this, we’ll need to set an environment variable.

Add the following to frontend/.env.

SKIP_PREFLIGHT_CHECK=true

Loading SST Environment Variables

We also want to load the environment variables from our backend. To do this, we’ll be using the @serverless-stack/static-site-env package. It’ll find the environment variables from our SST app and load it while starting the React development environment.

Run the following in the frontend/ directory.

$ npm install @serverless-stack/static-site-env --save-dev

Now to use this package, we’ll add it to our package.json scripts.

Replace the start script in your frontend/package.json.

"start": "react-scripts start",

With.

"start": "sst-env -- react-scripts start",

Add the React App to SST

We are going to be deploying our React app to AWS. To do that we’ll be using the SST ReactStaticSite construct.

Create a new file in lib/FrontendStack.js and add the following.

import * as sst from "@serverless-stack/resources";

export default class FrontendStack extends sst.Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    const { api, auth, bucket } = props;

    // Define our React app
    const site = new sst.ReactStaticSite(this, "ReactSite", {
      path: "frontend",
      // Pass in our environment variables
      environment: {
        REACT_APP_API_URL: api.url,
        REACT_APP_REGION: scope.region,
        REACT_APP_BUCKET: bucket.bucketName,
        REACT_APP_USER_POOL_ID: auth.cognitoUserPool.userPoolId,
        REACT_APP_IDENTITY_POOL_ID: auth.cognitoCfnIdentityPool.ref,
        REACT_APP_USER_POOL_CLIENT_ID:
          auth.cognitoUserPoolClient.userPoolClientId,
      },
    });

    // Show the url in the output
    this.addOutputs({
      SiteUrl: site.url,
    });
  }
}

We are creating a new stack in SST. We could’ve used one of the existing stacks but this allows us to show how to connect stacks together.

We are doing a couple of things of note here:

  1. We are pointing our ReactStaticSite construct to the frontend/ directory where our React app is.
  2. We are passing in the outputs from our other stacks as environment variables in React. This means that we won’t have to hard code them in our React app. You can read more about this over in our chapter on, Setting serverless environments variables in a React app.
  3. And finally, we are outputting out the URL of our React app.

Adding to the app

Let’s add this new stack to the rest of our app.

Replace the main function in lib/index.js with.

export default function main(app) {
  const storageStack = new StorageStack(app, "storage");

  const apiStack = new ApiStack(app, "api", {
    table: storageStack.table,
  });

  const authStack = new AuthStack(app, "auth", {
    api: apiStack.api,
    bucket: storageStack.bucket,
  });

  new FrontendStack(app, "frontend", {
    api: apiStack.api,
    auth: authStack.auth,
    bucket: storageStack.bucket,
  });
}

Here you’ll notice that we are passing in the references from our other stacks into the FrontendStack.

Also, import the new stack at the top.

import FrontendStack from "./FrontendStack";

Deploy the Changes

If you switch over to your terminal, you’ll notice that you are being prompted to redeploy your changes. Go ahead and hit ENTER.

Note that, you’ll need to have sst start running for this to happen. If you had previously stopped it, then running npx sst start will deploy your changes again.

You should see that the new frontend stack has been deployed.

Stack dev-notes-frontend
  Status: deployed
  Outputs:
    SiteUrl: https://d3j4c16hczgtjw.cloudfront.net
  ReactSite:
    REACT_APP_API_URL: https://5bv7x0iuga.execute-api.us-east-1.amazonaws.com
    REACT_APP_BUCKET: dev-notes-storage-uploadsbucketc4b27cc7-xmqzx69e5bpt
    REACT_APP_IDENTITY_POOL_ID: us-east-1:2d7b425d-eb44-4c42-afbd-645018b37a27
    REACT_APP_REGION: us-east-1
    REACT_APP_USER_POOL_CLIENT_ID: jbf2qe4h17tl2u94fntkjii7n
    REACT_APP_USER_POOL_ID: us-east-1_gll8EbWrr

Start the React App

Let’s start our React development environment.

In the frontend/ directory run.

$ npm start

This should fire up the newly created app in your browser.

New Create React App screenshot

Change the Title

Let’s quickly change the title of our note taking app. Open up public/index.html and edit the title tag to the following:

<title>Scratch - A simple note taking app</title>

Create React App comes pre-loaded with a pretty convenient yet minimal development environment. It includes live reloading, a testing framework, ES6 support, and much more.

Now we are ready to build our frontend! We are going to start by creating our app icon and updating the favicons.