Skip to main content

NextjsSite

The NextjsSite construct is a higher level CDK construct that makes it easy to create a Next.js app. It provides a simple way to build and deploy the site to an S3 bucket; setup a CloudFront CDN for fast content delivery; and configure a custom domain for the website URL.

It also allows you to automatically set the environment variables in your Next.js app directly from the outputs in your SST app.

Most of the Next.js features are supported, including:

Initializer

new NextjsSite(scope: Construct, id: string, props: NextjsSiteProps)

Parameters

Examples

The NextjsSite construct is designed to make it easy to work with Next.js apps.

Creating a Next.js app

Deploys a Next.js app in the path/to/site directory.

new NextjsSite(this, "NextSite", {
path: "path/to/site",
});

Configuring environment variables

The NextjsSite construct allows you to set the environment variables in your Next.js app based on outputs from other constructs in your SST app. So you don't have to hard code the config from your backend. Let's look at how.

Next.js supports setting build time environment variables. In your JS files this looks like:

pages/index.js
console.log(process.env.API_URL);
console.log(process.env.USER_POOL_CLIENT);

You can pass these in directly from the construct.

new NextjsSite(this, "NextSite", {
path: "path/to/site",
environment: {
API_URL: api.url,
USER_POOL_CLIENT: auth.cognitoUserPoolClient.userPoolClientId,
},
});

Where api.url or auth.cognitoUserPoolClient.userPoolClientId are coming from other constructs in your SST app.

While deploying

On sst deploy, the environment variables will first be replaced by placeholder values, {{ API_URL }} and {{ USER_POOL_CLIENT }}, when building the Next.js app. And after the referenced resources have been created, the Api and User Pool in this case, the placeholders in the HTML and JS files will then be replaced with the actual values.

caution

Since the actual values are determined at deploy time, you should not rely on the values at build time. For example, you cannot fetch from process.env.API_URL inside getStaticProps() at build time.

There are a couple of work arounds:

  • Hardcode the API URL
  • Read the API URL dynamically at build time (ie. from an SSM value)
  • Use fallback pages to generate the page on the fly

While developing

To use these values while developing, run sst start to start the Live Lambda Development environment.

npx sst start

Then in your Next.js app to reference these variables, add the sst-env package.

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

And tweak the Next.js dev script to:

package.json
"scripts": {
"dev": "sst-env -- next dev",
"build": "next build",
"start": "next start"
},

Now you can start your Next.js app as usual and it'll have the environment variables from your SST app.

npm run dev

There are a couple of things happening behind the scenes here:

  1. The sst start command generates a file with the values specified by the NextjsSite construct's environment prop.
  2. The sst-env CLI will traverse up the directories to look for the root of your SST app.
  3. It'll then find the file that's generated in step 1.
  4. It'll load these as environment variables before running the start command.
note

sst-env only works if the Next.js app is located inside the SST app or inside one of its subdirectories. For example:

/
sst.json
nextjs-app/

Configuring custom domains

You can configure the website with a custom domain hosted either on Route 53 or externally.

Using the basic config (Route 53 domains)

new NextjsSite(this, "Site", {
path: "path/to/site",
customDomain: "domain.com",
});

Redirect www to non-www (Route 53 domains)

new NextjsSite(this, "Site", {
path: "path/to/site",
customDomain: {
domainName: "domain.com",
domainAlias: "www.domain.com",
},
});

Configuring domains across stages (Route 53 domains)

export default class MyStack extends Stack {
constructor(scope, id, props) {
super(scope, id, props);

new NextjsSite(this, "Site", {
path: "path/to/site",
customDomain: {
domainName:
scope.stage === "prod" ? "domain.com" : `${scope.stage}.domain.com`,
domainAlias: scope.stage === "prod" ? "www.domain.com" : undefined,
},
});
}
}

Using the full config (Route 53 domains)

new NextjsSite(this, "Site", {
path: "path/to/site",
customDomain: {
domainName: "domain.com",
domainAlias: "www.domain.com",
hostedZone: "domain.com",
},
});

Importing an existing certificate (Route 53 domains)

import { Certificate } from "@aws-cdk/aws-certificatemanager";

new NextjsSite(this, "Site", {
path: "path/to/site",
customDomain: {
domainName: "domain.com",
certificate: Certificate.fromCertificateArn(this, "MyCert", certArn),
},
});

Note that, the certificate needs be created in the us-east-1(N. Virginia) region as required by AWS CloudFront.

Specifying a hosted zone (Route 53 domains)

If you have multiple hosted zones for a given domain, you can choose the one you want to use to configure the domain.

import { HostedZone } from "@aws-cdk/aws-route53";

new NextjsSite(this, "Site", {
path: "path/to/site",
customDomain: {
domainName: "domain.com",
hostedZone: HostedZone.fromHostedZoneAttributes(this, "MyZone", {
hostedZoneId,
zoneName,
}),
},
});

Configuring externally hosted domain

import { Certificate } from "@aws-cdk/aws-certificatemanager";

new NextjsSite(this, "Site", {
path: "path/to/site",
customDomain: {
isExternalDomain: true,
domainName: "domain.com",
certificate: Certificate.fromCertificateArn(this, "MyCert", certArn),
},
});

Note that the certificate needs be created in the us-east-1(N. Virginia) region as required by AWS CloudFront, and validated. After the Distribution has been created, create a CNAME DNS record for your domain name with the Distribution's URL as the value. Here are more details on configuring SSL Certificate on externally hosted domains.

Also note that you can also migrate externally hosted domains to Route 53 by following this guide.

Configuring the Edge Functions

Configure the internally created CDK Lambda@Edge Function instance.

new NextjsSite(this, "Site", {
path: "path/to/site",
defaultFunctionProps: {
timeout: 20,
memorySize: 2048,
permissions: ["sns"],
},
});

Attaching permissions

You can attach a set of permissions to allow the Next.js API routes and Server Side rendering getServerSideProps to access other AWS resources.

const site = new NextjsSite(this, "Site", {
path: "path/to/site",
});

site.attachPermissions(["sns"]);

Reusing CloudFront cache policies

CloudFront has a limit of 20 cache policies per AWS account. This is a hard limit, and cannot be increased. Each NextjsSite creates 3 cache policies. If you plan to deploy multiple Next.js sites, you can have the constructs share the same cache policies by reusing them across sites.

import * as cloudfront from "@aws-cdk/aws-cloudfront";

const cachePolicies = {
staticCachePolicy: new cloudfront.CachePolicy(this, "StaticCache", NextjsSite.staticCachePolicyProps),
imageCachePolicy: new cloudfront.CachePolicy(this, "ImageCache", NextjsSite.imageCachePolicyProps),
lambdaCachePolicy: new cloudfront.CachePolicy(this, "LambdaCache", NextjsSite.lambdaCachePolicyProps),
};

new NextjsSite(this, "Site1", {
path: "path/to/site1",
cfCachePolicies: cachePolicies,
});

new NextjsSite(this, "Site2", {
path: "path/to/site2",
cfCachePolicies: cachePolicies,
});

Properties

An instance of NextjsSite contains the following properties.

static staticCachePolicyProps

Type : cloudfront.CachePolicyProps

The default CloudFront cache policy properties for static pages.

static imageCachePolicyProps

Type : cloudfront.CachePolicyProps

The default CloudFront cache policy properties for images.

static lambdaCachePolicyProps

Type : cloudfront.CachePolicyProps

The default CloudFront cache policy properties for Lambda@Edge.

url

Type: string

The CloudFront URL of the website.

customDomainUrl?

Type: string

If the custom domain is enabled, this is the URL of the website with the custom domain.

bucketArn

Type: string

The ARN of the internally created CDK Bucket instance.

bucketName

Type: string

The name of the internally created CDK Bucket instance.

distributionId

Type: string

The ID of the internally created CDK Distribution instance.

distributionDomain

Type: string

The domain name of the internally created CDK Distribution instance.

s3Bucket

Type : cdk.aws-s3.Bucket

The internally created CDK Bucket instance.

cfDistribution

Type : cdk.aws-cloudfront.Distribution

The internally created CDK Distribution instance.

Methods

An instance of NextjsSite contains the following methods.

attachPermissions

attachPermissions(permissions: Permissions)

Parameters

Attaches the given list of permissions to allow the Next.js API routes and Server Side rendering getServerSideProps to access other AWS resources.

NextjsSiteProps

path

Type : string

Path to the directory where the website source is located.

customDomain?

Type : string | NextjsSiteDomainProps

The customDomain for this website. SST supports domains that are hosted either on Route 53 or externally.

Note that you can also migrate externally hosted domains to Route 53 by following this guide.

Takes either the domain as a string.

"domain.com"

Or the NextjsSiteDomainProps.

{
domainName: "domain.com",
domainAlias: "www.domain.com",
hostedZone: "domain.com",
}

environment?

Type : { [key: string]: string }

An associative array with the key being the environment variable name.

{
API_URL: api.url
}

s3Bucket?

Type: cdk.aws-s3.BucketProps

Pass in a cdk.aws-s3.BucketProps value to override the default settings this construct uses to create the CDK Bucket internally.

cfDistribution?

Type: NextjsSiteCdkDistributionProps

Pass in a NextjsSiteCdkDistributionProps value to override the default settings this construct uses to create the CDK Distribution internally.

cfCachePolicies?

Type: NextjsSiteCachePolicyProps

Pass in a NextjsSiteCachePolicyProps value to override the default CloudFront cache policies created internally.

defaultFunctionProps?

Type: NextjsSiteFunctionProps, defaults to {}

The default function props to be applied to all the Lambda@Edge functions created by this construct.

disablePlaceholder?

Type : boolean, defaults to false

When running sst start, a placeholder site is deployed. This is to ensure that the site content remains unchanged, and subsequent sst start can start up quickly.

NextjsSiteDomainProps

domainName

Type : string

The domain to be assigned to the website URL (ie. domain.com).

Supports domains that are hosted either on Route 53 or externally.

domainAlias?

Type : string, defaults to no alias configured

An alternative domain to be assigned to the website URL. Visitors to the alias will be redirected to the main domain. (ie. www.domain.com).

Use this to create a www. version of your domain and redirect visitors to the root domain.

hostedZone?

Type : string | cdk.aws-route53.IHostedZone, defaults to the domain name

The hosted zone in Route 53 that contains the domain. Takes the name of the hosted zone as a string or the hosted zone construct cdk.aws-route53.HostedZone. By default, SST will look for a hosted zone matching the domainName that's passed in.

Set this option if SST cannot find the hosted zone in Route 53.

certificate?

Type : cdk.aws-certificatemanager.ICertificate, defaults to undefined

The certificate for the domain. By default, SST will create a certificate with the domain name from the domainName option. The certificate will be created in the us-east-1(N. Virginia) region as required by AWS CloudFront.

Set this option if you have an existing certificate in the us-east-1 region in AWS Certificate Manager you want to use.

isExternalDomain?

Type : boolean, defaults to false

Set this option if the domain is not hosted on Amazon Route 53.

NextjsSiteFunctionProps

timeout?

Type : number, defaults to 10

Lambda@Edge function execution timeout in seconds.

memorySize?

Type : number, defaults to 1024

The amount of memory in MB allocated to this Lambda@Edge function.

permissions?

Type : Permissions, defaults to []

Attaches the given list of permissions to the function.

NextjsSiteCachePolicyProps

staticCachePolicy?

Type : cloudfront.ICachePolicy, defaults to created using staticCachePolicyProps

CloudFront cache policy for static pages.

imageCachePolicy?

Type : cloudfront.ICachePolicy, defaults to created using imageCachePolicyProps

CloudFront cache policy for images.

lambdaCachePolicy?

Type : cloudfront.ICachePolicy, defaults to created using lambdaCachePolicyProps

CloudFront cache policy for Lambda@Edge.

NextjsSiteCdkDistributionProps

NextjsSiteCdkDistributionProps extends cdk.aws-cloudfront.DistributionProps with the exception that the defaultBehavior field is optional and takes a cdk.aws-cloudfront.AddBehaviorOptions object.

You can use NextjsSiteCdkDistributionProps to configure the CloudFront distribution properties.