NextjsSite
caution
This is the SST v1.x Constructs doc. SST v2 is now released. If you are using v2, see the v2 Constructs doc. If you are looking to upgrade to v2, check out the upgrade steps.
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.
Next.js Features
The NextjsSite
construct uses the @sls-next/lambda-at-edge
package from the serverless-next.js
project to build and package your Next.js app so that it can be deployed to Lambda@Edge and CloudFront.
note
To use the NextjsSite
construct, you have to install @sls-next/lambda-at-edge
as a dependency in your package.json
.
npm install --save @sls-next/lambda-at-edge
Most of the Next.js 11 features are supported, including:
- Static Site Generation (SSG): Static pages are served out through the CloudFront CDN.
- Server Side Rendering (SSR): Server side rendering is performed at CloudFront edge locations using Lambda@Edge.
- API Routes: API requests are served from CloudFront edge locations using Lambda@Edge.
- Incremental Static Regeneration (ISR): Regeneration is performed using Lambda functions, and the generated pages will be served out through the CloudFront CDN.
- Image Optimization: Images are resized and optimized at CloudFront edge locations using Lambda@Edge.
Next.js 12 features like middleware and AVIF image are not yet supported. You can read more about the features supported by serverless-next.js
. And you can follow the progress on Next.js 12 support here.
Examples
Creating a Next.js app
Deploys a Next.js app in the path/to/site
directory.
new NextjsSite(stack, "NextSite", {
path: "path/to/site",
});
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:
console.log(process.env.API_URL);
console.log(process.env.USER_POOL_CLIENT);
You can pass these in directly from the construct.
new NextjsSite(stack, "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:
"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:
- The
sst start
command generates a file with the values specified by theNextjsSite
construct'senvironment
prop. - The
sst-env
CLI will traverse up the directories to look for the root of your SST app. - It'll then find the file that's generated in step 1.
- It'll load these as environment variables before running the start command.
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/
:::
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(stack, "Site", {
path: "path/to/site",
customDomain: "domain.com",
});
Redirect www to non-www (Route 53 domains)
new NextjsSite(stack, "Site", {
path: "path/to/site",
customDomain: {
domainName: "domain.com",
domainAlias: "www.domain.com",
},
});
Configuring domains across stages (Route 53 domains)
new NextjsSite(stack, "Site", {
path: "path/to/site",
customDomain: {
domainName:
scope.stage === "prod" ? "domain.com" : `${scope.stage}.domain.com`,
domainAlias: scope.stage === "prod" ? "www.domain.com" : undefined,
},
});
Configuring alternate domain names (Route 53 domains)
You can specify additional domain names for the site url. Note that the certificate for these names will not be automatically generated, so the certificate option must be specified. Also note that you need to manually create the Route 53 records for the alternate domain names.
import * as acm from "aws-cdk-lib/aws-certificatemanager";
import * as route53 from "aws-cdk-lib/aws-route53";
import * as route53Targets from "aws-cdk-lib/aws-route53-targets";
// Look up hosted zone
const hostedZone = route53.HostedZone.fromLookup(stack, "HostedZone", {
domainName: "domain.com",
});
// Create a certificate with alternate domain names
const certificate = new acm.DnsValidatedCertificate(stack, "Certificate", {
domainName: "foo.domain.com",
hostedZone,
region: "us-east-1",
subjectAlternativeNames: ["bar.domain.com"],
});
// Create site
const site = new NextjsSite(stack, "Site", {
path: "path/to/site",
customDomain: {
domainName: "foo.domain.com",
alternateNames: ["bar.domain.com"],
cdk: {
hostedZone,
certificate,
},
},
});
// Create A and AAAA records for the alternate domain names
const recordProps = {
recordName: "bar.domain.com",
zone: hostedZone,
target: route53.RecordTarget.fromAlias(
new route53Targets.CloudFrontTarget(site.cdk.distribution)
),
};
new route53.ARecord(stack, "AlternateARecord", recordProps);
new route53.AaaaRecord(stack, "AlternateAAAARecord", recordProps);
Importing an existing certificate (Route 53 domains)
import { Certificate } from "aws-cdk-lib/aws-certificatemanager";
new NextjsSite(stack, "Site", {
path: "path/to/site",
customDomain: {
domainName: "domain.com",
cdk: {
certificate: Certificate.fromCertificateArn(stack, "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-lib/aws-route53";
new NextjsSite(stack, "Site", {
path: "path/to/site",
customDomain: {
domainName: "domain.com",
cdk: {
hostedZone: HostedZone.fromHostedZoneAttributes(stack, "MyZone", {
hostedZoneId,
zoneName,
}),
},
},
});
Configuring externally hosted domain
import { Certificate } from "aws-cdk-lib/aws-certificatemanager";
new NextjsSite(stack, "Site", {
path: "path/to/site",
customDomain: {
isExternalDomain: true,
domainName: "domain.com",
cdk: {
certificate: Certificate.fromCertificateArn(stack, "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 Lambda Functions
Configure the internally created CDK Lambda Function
instance.
new NextjsSite(stack, "Site", {
path: "path/to/site",
defaults: {
function: {
timeout: 20,
memorySize: 2048,
permissions: ["sns"],
},
},
});
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(stack, "Site", {
path: "path/to/site",
});
site.attachPermissions(["sns"]);
Advanced examples
Using an existing S3 Bucket
import * as s3 from "aws-cdk-lib/aws-s3";
new NextjsSite(stack, "Site", {
path: "path/to/site",
cdk: {
bucket: s3.Bucket.fromBucketName(stack, "Bucket", "my-bucket"),
},
});
Configuring Lambda Functions
Configure the internally created CDK Lambda Function
instance.
new NextjsSite(stack, "Site", {
path: "path/to/site",
defaults: {
function: {
timeout: 20,
memorySize: 2048,
permissions: ["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-lib/aws-cloudfront";
const cachePolicies = {
staticCachePolicy: new cloudfront.CachePolicy(
stack,
"StaticCache",
NextjsSite.staticCachePolicyProps
),
imageCachePolicy: new cloudfront.CachePolicy(
stack,
"ImageCache",
NextjsSite.imageCachePolicyProps
),
lambdaCachePolicy: new cloudfront.CachePolicy(
stack,
"LambdaCache",
NextjsSite.lambdaCachePolicyProps
),
};
new NextjsSite(stack, "Site1", {
path: "path/to/site1",
cdk: {
cachePolicies,
},
});
new NextjsSite(stack, "Site2", {
path: "path/to/site2",
cdk: {
cachePolicies,
},
});
Reusing CloudFront image origin request policy
CloudFront has a limit of 20 origin request policies per AWS account. This is a hard limit, and cannot be increased. Each NextjsSite
creates a new origin request policy by default. If you plan to deploy multiple Next.js sites, you can have the constructs share the same origin request policy by reusing them across sites.
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
const imageOriginRequestPolicy = new cloudfront.OriginRequestPolicy(
stack,
"ImageOriginRequest",
NextjsSite.imageOriginRequestPolicyProps
);
new NextjsSite(stack, "Site1", {
path: "path/to/site1",
cdk: {
imageOriginRequestPolicy,
},
});
new NextjsSite(stack, "Site2", {
path: "path/to/site2",
cdk: {
imageOriginRequestPolicy,
},
});
Constructor
new NextjsSite(scope, id, props)
Parameters
- scope Construct
- id string
- props NextjsSiteProps
NextjsSiteProps
commandHooks.afterBuild?
Type : Array<string>
Commands to run after building the Next.js app. Commands are chained with &&
, and they are run inside the Next.js app folder.
new NextjsSite(stack, "NextSite", {
path: "path/to/site",
commandHooks: {
afterBuild: ["npx next-sitemap"],
}
});
customDomain?
Type : string | NextjsDomainProps
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.
new NextjsSite(stack, "Site", {
path: "path/to/site",
customDomain: "domain.com",
});
new NextjsSite(stack, "Site", {
path: "path/to/site",
customDomain: {
domainName: "domain.com",
domainAlias: "www.domain.com",
hostedZone: "domain.com"
},
});
defaults.function.memorySize?
Type : number
defaults.function.permissions?
Type : Permissions
defaults.function.runtime?
Type : "nodejs16.x" | "nodejs12.x" | "nodejs14.x"
Default : "nodejs16.x"
The runtime environment.
new NextjsSite(stack, "Function", {
path: "path/to/site",
runtime: "nodejs16.x",
})
defaults.function.timeout?
Type : number
disablePlaceholder?
Type : boolean
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.
new NextjsSite(stack, "NextSite", {
path: "path/to/site",
disablePlaceholder: true,
});
An object with the key being the environment variable name.
new NextjsSite(stack, "NextSite", {
path: "path/to/site",
environment: {
API_URL: api.url,
USER_POOL_CLIENT: auth.cognitoUserPoolClient.userPoolClientId,
},
});
nextBinPath?
Type : string
Default : "./node_modules/.bin/next"
Path to the next executable, typically in node_modules. This should be used if next is installed in a non-standard location.
path
Type : string
Path to the directory where the website source is located.
waitForInvalidation?
Type : boolean
While deploying, SST waits for the CloudFront cache invalidation process to finish. This ensures that the new content will be served once the deploy command finishes. However, this process can sometimes take more than 5 mins. For non-prod environments it might make sense to pass in false
. That'll skip waiting for the cache to invalidate and speed up the deploy process.
cdk.bucket?
Type : IBucket | BucketProps
Allows you to override default settings this construct uses internally to ceate the bucket
cdk.cachePolicies.imageCachePolicy?
Type : ICachePolicy
cdk.cachePolicies.lambdaCachePolicy?
Type : ICachePolicy
cdk.cachePolicies.staticCachePolicy?
Type : ICachePolicy
Override the default CloudFront cache policies created internally.
cdk.distribution?
Type : NextjsCdkDistributionProps
Pass in a value to override the default settings this construct uses to create the CDK Distribution
internally.
cdk.id?
Type : string
Allows you to override default id for this construct.
cdk.imageOriginRequestPolicy?
Type : IOriginRequestPolicy
Override the default CloudFront image origin request policy created internally
cdk.regenerationQueue?
Type : QueueProps
Override the default settings this construct uses to create the CDK Queue
internally.
Properties
An instance of NextjsSite
has the following properties.
bucketArn
Type : string
The ARN of the internally created S3 Bucket.
bucketName
Type : string
The name of the internally created S3 Bucket.
customDomainUrl
Type : undefined | string
If the custom domain is enabled, this is the URL of the website with the custom domain.
distributionDomain
Type : string
The domain name of the internally created CloudFront Distribution.
distributionId
Type : string
The ID of the internally created CloudFront Distribution.
id
Type : string
imageCachePolicyProps
Type : CachePolicyProps
The default CloudFront cache policy properties for images.
imageOriginRequestPolicyProps
Type : OriginRequestPolicyProps
The default CloudFront image origin request policy properties for Lambda@Edge.
lambdaCachePolicyProps
Type : CachePolicyProps
The default CloudFront cache policy properties for Lambda@Edge.
staticCachePolicyProps
Type : CachePolicyProps
The default CloudFront cache policy properties for static pages.
url
Type : string
The CloudFront URL of the website.
cdk.bucket
Type : Bucket
The internally created CDK Bucket
instance.
cdk.certificate?
Type : ICertificate
The AWS Certificate Manager certificate for the custom domain.
cdk.distribution
Type : Distribution
The internally created CDK Distribution
instance.
cdk.hostedZone?
Type : IHostedZone
The Route 53 hosted zone for the custom domain.
cdk.regenerationQueue
Type : Queue
The internally created CDK Queue
instance.
Methods
An instance of NextjsSite
has the following methods.
attachPermissions
attachPermissions(permissions)
Parameters
- permissions Permissions
Attaches the given list of permissions to allow the Next.js API routes and Server Side rendering getServerSideProps
to access other AWS resources.
Attaching permissions
const site = new NextjsSite(stack, "Site", {
path: "path/to/site",
});
site.attachPermissions(["sns"]);
NextjsDomainProps
alternateNames?
Type : Array<string>
Default : []
Specify additional names that should route to the Cloudfront Distribution. Note, certificates for these names will not be automatically generated so the certificate
option must be specified.
domainAlias?
Type : string
Default : 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.
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.
hostedZone?
Type : string
Default : same as the domainName
The hosted zone in Route 53 that contains the domain. 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.
isExternalDomain?
Type : boolean
Default : false
Set this option if the domain is not hosted on Amazon Route 53.
cdk.certificate?
Type : ICertificate
Import the certificate for the domain. By default, SST will create a certificate with the domain name. 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.
cdk.hostedZone?
Type : IHostedZone
Import the underlying Route 53 hosted zone.
NextjsCdkDistributionProps
defaultBehavior?
Type :