This guide shows you how to deploy a Next.js application to Edgio.
Example
Next.js Commerce
For details on using the Next.js Commerce template with Edgio, refer to our Next.js Commerce Guide.
Connector
Edgio provides a connector for this framework. Learn more.
Supported Versions
Edgio supports Next.js version 9 through 13.
Supported Features
Edgio supports all of the most powerful features of Next.js, including:
- Static Site Generation (SSG)
- Server Side Rendering (SSR)
- Incremental Static Regeneration (ISR)
- Localization
- Image Optimization
getStaticPaths
(includingfallback: (true|false|'blocking')
)getStaticProps
(includingrevalidate
)getServerSideProps
getInitialProps
Prerequisites
Setup requires:
- An Edgio account. Sign up for free.
- An Edgio property. Learn how to create a property.
- Node.js. View supported versions and installation steps.
- Edgio CLI.
Install the Edgio CLI
If you have not already done so, install the Edgio CLI.
Bash
1npm i -g @edgio/cli@^6.0.0
When installing the Edgio CLI globally in a virtual environment that has Node and NPM installed globally, you may run into permission issues. In that case, you can install the Edgio CLI locally within your app using
npm i -D @edgio/cli
and running commands using ./node_modules/@edgio/cli
instead of edgio
.If you run into permission issues while attempting to install the Edgio CLI globally on your local development machine, these may be fixed by using nvm to manage Node and NPM.
Getting Started
Create a Next.js Application
If you don’t already have a Next.js application, you can create one using:
Bash
1npx create-next-app@latest
Initializing your Project
Initialize your project for use with Edgio by running the following command in your project’s root directory:
Bash
1cd my-next-app2edgio init --edgioVersion ^6.0.0
This will automatically add all of the required dependencies and files to your project. These include:
-
The
@edgio/cli
package - Allows you to control Edgio through project-local CLI. -
The
@edgio/core
package - Allows you to declare routes and deploy your application to Edgio. -
The
@edgio/prefetch
package - Allows you to configure a service worker to prefetch and cache pages to improve browsing speed. -
The
@edgio/devtools
package - Allows you to monitor the caching and prefetching functionality. -
The
@edgio/next
package - Provides router middleware that automatically adds Next.js pages and api routes to the Edgio router. -
The
@edgio/react
package - Provides aPrefetch
component for prefetching pages.edgio.config.js
- Contains various configuration options for Edgio.
-
routes.js
- A default routes file that proxies all requests to the server. Update this file to add caching or proxy some URLs to a different origin. -
sw/service-worker.js
- A service worker implemented using Workbox.
Next.js Config Plugins
If your project does not have a
next.config.js
file, one will automatically be added when you run edgio init
. Doing so adds two plugins:withEdgio
(required)withServiceWorker
(optional)
If your project already has this config file, you need to add these plugins yourself.
JavaScriptnext.config.js
1const { withEdgio, withServiceWorker } = require('@edgio/next/config')23module.exports = withEdgio(4 withServiceWorker({5 // Output source maps so that stack traces have original source filenames and line numbers when tailing6 // the logs in the Edgio Developer console.7 edgioSourceMaps: true,8 })9)
withEdgio
The
withEdgio
plugin optimizes the Next.js build for running on Edgio. It is required to deploy your application on Edgio and accepts the following parameters:edgioSourceMaps
: Defaults tofalse
. Set totrue
to add server-side source maps so that stack traces have original source filenames and line numbers when tailing the logs in the Edgio Developer console. This will increase the serverless bundle size but will not affect performance. If you find that your app exceeds the maximum serverless bundle size allowed by Edgio, you can disable this option to conserve space.
We noticed some performance issues related to sourcemaps being loaded in our
Serverless infrastructure, which may result in 539 project timeout errors. In
case you encounter such errors, please try again with sourcemaps disabled.
This document will be updated once the problem is fully resolved.
withServiceWorker
The
withServiceWorker
plugin builds a service worker from sw/service-worker.js
that prefetches and caches all static JS assets and enables Edgio’s prefetching functionality.Edgio Devtools
By default, Devtools are enabled on production builds of Next.js with Edgio. To disable devtools in production, add the
disableEdgioDevTools
flag:JavaScriptnext.config.js
1const { withEdgio, withServiceWorker } = require('@edgio/next/config')23module.exports = withEdgio(4 withServiceWorker({5 // Output source maps so that stack traces have original source filenames and line numbers when tailing6 // the logs in the Edgio Developer console.7 edgioSourceMaps: true,8 // Don't include Edgio Devtools in production9 // More on Edgio Devtools at https://docs.edg.io/applications/performance/observability/devtools10 disableEdgioDevTools: true,11 })12)
Running Locally
Test your app with the Sites on your local machine by running the following command in your project’s root directory:
Bash
1edgio dev
Deploying
Deploy your app to the Sites by running the following command in your project’s root directory:
Bash
1edgio deploy
See Deployments for more information.
Prefetching
The
edgio init
command adds a service worker based on Workbox at sw/service-worker.js
.
If you have an existing service worker that uses workbox, you can copy its contents into sw/service-worker.js
and simply add the following to your service worker:JavaScriptsw/service-worker.js
1import { Prefetcher } from '@edgio/prefetch/sw';23new Prefetcher().route();
The above code allows you to prefetch pages from Edgio’s edge cache to significantly improve browsing speed. To prefetch a page, add the
Prefetch
component from @edgio/react
to any Next.js Link
element. The following example shows you how to prefetch JSON data from getServerSideProps
or getStaticProps
using the createNextDataUrl
function from @edgio/next/client
.JavaScript
1import {Prefetch} from '@edgio/react';2import Link from 'next/link';3import {useRouter} from 'next/router';4import {createNextDataURL} from '@edgio/next/client';56export default function ProductListing({products}) {7 const {locale} = useRouter(); // you can omit this if you're not using localization89 return (10 <ul>11 {products.map((product, i) => (12 <li key={i}>13 <Link href={product.url} passHref>14 <Prefetch15 url={createNextDataURL({16 href: product.url,17 locale, // you can omit this if you're not using localization18 routeParams: {19 // keys must match the param names in your next page routes20 // So for example if your product page is /products/[productId].js:21 productId: product.id,22 },23 })}>24 <a>25 <img src={product.thumbnail} />26 </a>27 </Prefetch>28 </Link>29 </li>30 ))}31 </ul>32 );33}3435export async function getServerSideProps({params: {id}}) {36 const products = await fetch(/* fetch from your api */).then((res) =>37 res.json()38 );3940 return {41 props: {42 products,43 },44 };45}
The
Prefetch
component fetches data for the linked page from Edgio’s edge cache and adds it to the service worker’s cache when the link becomes visible in the viewport. When the user taps on the link, the page transition will be instantaneous because the browser won’t need to fetch data from the network.Routing
Edgio supports Next.js’s built-in routing scheme for both page and API routes, including Next.js 9’s clean dynamic routes. The default
routes.js
file created by edgio init
sends all requests to Next.js via a fallback route:JavaScriptroutes.js
1import {nextRoutes} from '@edgio/next';2import {Router} from '@edgio/core/router';34export default new Router()5 .get('/service-worker.js', ({cache, serveStatic}) => {6 cache({7 edge: {8 maxAgeSeconds: 60 * 60 * 24 * 365,9 },10 });11 serveStatic('.next/static/service-worker.js');12 })13 .use(nextRoutes);
Preview Mode
To be able to use Preview Mode while being able to cache the respective pages, update your routes to match the requests that contain the two cookies
__prerender_bypass
& __next_preview_data
, and send those to serverless for rendering.JavaScriptroutes.js
1import {Router} from '@edgio/core/router';2import {nextRoutes, renderNextPage} from '@edgio/next';34export default new Router()5 .match(6 {7 path: '/:path*',8 cookies: {9 __prerender_bypass: /.*/g,10 __next_preview_data: /.*/g,11 },12 },13 ({cache, renderWithApp}) => {14 cache({15 edge: false,16 browser: false,17 });18 renderNextPage('/:path*', res); // In case you're using Next.js < v1219 // renderWithApp() // In case you're using Next.js >= v1220 }21 )22 .get('/service-worker.js', ({cache, serveStatic}) => {23 cache({24 edge: {25 maxAgeSeconds: 60 * 60 * 24 * 365,26 },27 });28 serveStatic('.next/static/service-worker.js');29 })30 .use(nextRoutes);
nextRoutes
In the above code,
nextRoutes
adds all Next.js routes to the router based on the /pages
directory. You can add additional routes before and after nextRoutes
. For example, you can choose to send some URLs to an alternate backend. This is useful for gradually replacing an existing site with a new Next.js app.A popular use case is to fallback to a legacy site for any route that your Next.js app isn’t configured to handle:
JavaScriptroutes.js
1import {nextRoutes} from '@edgio/next';2import {Router} from '@edgio/core/router';34export default new Router().use(nextRoutes);5match('/:path*', ({proxy}) => proxy('legacy'));
To configure the legacy backend, use edgio.config.js:
JavaScriptedgio.config.js
1module.exports = {2 backends: {3 legacy: {4 domainOrIp: process.env.LEGACY_BACKEND_DOMAIN || 'legacy.my-site.com',5 hostHeader:6 process.env.LEGACY_BACKEND_HOST_HEADER || 'legacy.my-site.com',7 },8 },9};
Using environment variables here allows you to configure different legacy domains for each Edgio environment.
rewrites and redirects
The
nextRoutes
plugin automatically adds routes for rewrites and redirects specified in next.config.js
. Redirects are served directly from the network edge to maximize performance.Caching
The easiest way to add edge caching to your Next.js app is to add caching routes before
nextRoutes
. For example,
imagine you have /pages/p/[productId].js
. Here’s how you can SSR responses as well as cache calls to getServerSideProps
:JavaScriptroutes.js
1export default new Router()2 // Products - SSR3 .get('/p/:productId', ({cache}) => {4 cache({5 // Caching it only on the edge6 edge: {7 maxAgeSeconds: 60 * 60 * 24,8 staleWhileRevalidateSeconds: 60 * 60,9 },10 });11 })12 // Products - getServerSideProps13 .get('/_next/data/:version/p/:productId.json', ({cache}) => {14 cache({15 // Allowing service worker (if present) to serve the cached responses from the browser itself16 browser: {17 maxAgeSeconds: 0,18 serviceWorkerSeconds: 60 * 60 * 24,19 },20 edge: {21 maxAgeSeconds: 60 * 60 * 24,22 staleWhileRevalidateSeconds: 60 * 60,23 },24 });25 })26 .use(nextRoutes);
Preventing Next.js pages from being cached by other CDNs
By default, Next.js adds a
cache-control: private, no-cache, no-store, must-revalidate
header to all responses from getServerSideProps
. The presence of private
would prevent Edgio from caching the response, so nextRoutes
from @edgio/next
automatically removes the private
portion of the header to enable caching at the edge. If you want your responses to be private, you need to specify a cache-control
header using the router:JavaScriptroutes.js
1new Router().get('/my-private-page', ({setResponseHeader}) => {2 setResponseHeader(3 'cache-control',4 'private, no-cache, no-store, must-revalidate'5 );6});
Doing so will prevent other CDNs running in front of Edgio from caching the response.
Using next-i18next
The next-i18next package is a popular solution for adding localization to Next.js apps. It has some issues when running in serverless deployments, but you can work around these:
First, you need to not use the default name for the
next-i18next.config.js
file. We recommend renaming it i18next.config.js
. When you use the default name, next-i18next will try to load the config when the serverless function starts and since it is not bundled with the app, it will fail.Then, you need to explicitly provide the config to
appWithTranslation
and serverSideTranslations
.So in your
pages/_app.js
:JavaScriptpages/_app.js
1export default appWithTranslation(MyApp, require('../i18next.config')); // <~ need to explicitly pass the config here
and in your pages:
JavaScript
1export async function getStaticProps({locale}) {2 return {3 props: {4 ...(await serverSideTranslations(5 locale,6 ['common', 'footer'],7 require('../i18next.config')8 )), // <~ need to explicitly pass the config here.9 // Will be passed to the page component as props10 },11 };12}
Make sure you also import the config correctly with the new name into your
next.config.js
:JavaScriptnext.config.js
1const { withEdgio, withServiceWorker } = require('@edgio/next/config')2const { i18n } = require('./i18next.config')34module.exports = withEdgio(5 withServiceWorker({6 i18n,7 }),8)
Finally, you will need to update your
edgio.config.js
to includeFiles where the locale files are stored. Example using the default of /public
:JavaScriptedgio.config.js
1module.exports = {2 connector: '@edgio/next',3 includeFiles: {4 public: true,5 },6};
A working example app can be found here.
Image optimizer
By default, Next.js image optimizer is replaced by our image optimizer, which is available in all build modes. You can disable it and use the built-in Next.js image optimizer instead by adding
disableImageOptimizer: true
to the edgio.config.js
file.JavaScriptedgio.config.js
1module.exports = {2 /* ... */3 disableImageOptimizer: true,4};
Note that Next.js apps built in serverless mode don’t include a Next.js image optimizer. Disabling our image optimizer without providing an alternative may cause them to fail.
Serverless Bundling
Next.js has continued to improve how it bundles production builds for deployment on serverless architectures. Edgio takes advantage of these improvementsby applying different configuration options depending on the version of Next.js being used:
Version | Next.js configs applied |
---|---|
Next.js < 12.2.0 | target: 'experimental-serverless-trace' |
Next.js >= 12.2.0 | output: 'standalone' |
For backwards compatibility, Edgio will also respect
target: 'serverless'
in your next.config.js for Next.js versions prior to 12.0.0.Note that NextRouter.render404 and renderNextPage are retired when using Next.js 12.2.0+. Requests are delegated to a Next.js server instance which will handle determining which page to render based on the request. Prior use cases should now be achieved via using Next.js redirects and rewrites.
Support for Next.js Middleware (BETA)
Edgio supports Next.js middleware starting with Next.js 12.2.0.
When using Next.js middleware it should be noted that the middleware functions are only executed at the serverless layer, after the edge cache. Middleware that you want to execute on each request needs to have caching disabled explicitly for the route on which the middleware is enabled. Some Middleware use cases such as rewriting the request to another route would be fine to cache. These use cases need to be evaluated on a per route basis with caching enabled/disabled based on the desired result.
Runtime Variables Configuration
Next.js Runtime Configuration requires Edgio 6.0.5+. See Upgrading.
Edgio supports runtime variables in
serverRuntimeConfig
and publicRuntimeConfig
properties of next.config.js
. For more information, visit the Next.js Runtime Configuration documentation.Additional Route Handling Options
These additional options allow you to change certain behavior of routes that are generated by
nextRoutes
.setEnforceTrailingSlash
Set this option to
true
to suppress the default Next.js behavior of removing trailing slashes when performing a redirect.If
trailingSlash: true
is set in next.config.js
, setting this option to false
, or removing it, will remove the redirect that adds the trailing slash.JavaScriptroutes.js
1import {nextRoutes} from '@edgio/next';2import {Router} from '@edgio/core/router';34nextRoutes.setEnforceTrailingSlash(true);56export default new Router().use(nextRoutes);
setQueryDuplicatesToArrayOnly
This option allows you to change the way Next.js duplicate query parameters are parsed by the
renderNextPage
function.By default, the duplicate query parameters are parsed as an array for the param name, as well as array notation. For example:
JavaScript
1const params = {2 query: {3 slug: ['value', 'value2'],4 'slug[0]': 'value',5 'slug[1]': 'value2',6 },7};
Set
setQueryDuplicatesToArrayOnly
to true
to parse them only as an array:JavaScript
1const params = {2 query: {3 slug: ['value', 'value2'],4 },5};
JavaScriptroutes.js
1import {nextRoutes} from '@edgio/next';2import {Router} from '@edgio/core/router';34nextRoutes.setQueryDuplicatesToArrayOnly(true);56export default new Router().use(nextRoutes);
This option has no effect on Next.js apps with Next.js 12 and newer, which are built in server mode.