Skip to main content
Frameworks

Next.js on Easel

Deploy App Router and Pages Router applications to Easel with support for server rendering, streaming, Server Actions, route handlers, proxy (middleware), image optimization, and framework-native caching.

Easel deploys standard Next.js applications with no adapter. Static output goes to the CDN, dynamic routes run in Functions, and the Next.js route and data caches use shared storage, so revalidation reaches every instance. For how the platform fits together, see How Easel works.

At a glance

Support levelProduction-ready
RoutersApp Router and Pages Router
RenderingStatic, SSR, streaming, ISR, and Partial Prerendering
Server runtimeNode.js
Adapter requiredNo
Automatic detectionYes
Most recently tested withNext.js 16.3.x

Deploy a Next.js application

Easel detects Next.js projects automatically. In most cases, you can deploy an existing application without changing its framework configuration.

Create a project and connect the Git repository, or deploy with the CLI:

easel deploy

Easel installs dependencies with the detected package manager, runs next build, and provisions what the build output needs. You can override the install command, build command, root directory, and Node.js version in build configuration.

What Easel deploys

Next.js outputEasel resource
Static pagesCDN assets
JavaScript, CSS, fonts, and other static filesCDN assets
Server Components and SSR routesEasel Functions
Route Handlers and API RoutesEasel Functions
Server ActionsEasel Functions
Proxy (middleware)Easel Function (US East)
ISR and cached route outputISR cache and CDN
next/image requestsImage optimization service
OpenTelemetry instrumentationEasel observability pipeline

Supported features

FeatureSupportNotes
App RouterSupportedIncludes layouts, Server Components, loading states, and error boundaries
Pages RouterSupportedIncludes pages, API Routes, and data-fetching methods
Static Site GenerationSupportedGenerated pages are deployed to the CDN
Server-Side RenderingSupportedDynamic routes run in Easel Functions
React Server ComponentsSupportedSupported through the App Router
StreamingSupportedDynamic responses stream without waiting for the complete render
Server ActionsSupportedRun in the application's server function
Route HandlersSupportedIncludes standard and streaming responses
API RoutesSupportedPages Router API Routes run in Functions
Incremental Static RegenerationSupportedIncludes time-based and on-demand revalidation
Cache ComponentsSupported'use cache: remote' is backed by Easel's regional Runtime Cache
Partial PrerenderingSupportedStatic shells and dynamic regions are deployed together
revalidatePathSupportedInvalidates matching route-cache entries
revalidateTagSupportedInvalidates data by cache tag
updateTagSupportedExpires tagged data for read-your-own-writes behavior
after()SupportedWork can continue after the response, within the function's duration
Proxy (middleware)SupportedRuns before the application route
Node.js runtimeSupportedUsed for server-rendered routes and server code
Edge runtime APIsSupportedAPI compatibility doesn't mean execution at every CDN location
next/imageSupportedRequests use Easel's image optimization service
Redirects, rewrites, and headersSupportedIncludes rules defined in next.config
Internationalized routingSupportedIncludes domain and subpath routing
Draft ModeSupportedPreview cookies are forwarded to the application runtime
OpenTelemetrySupportedFramework telemetry can be exported through Easel
Turbopack buildsSupportedUsed when enabled by the selected Next.js version
Static exportSupportedDeploys the exported application as a static site
WebSocketsNot supportedFunction requests use HTTP request-response semantics

Rendering

Routes generated during next build deploy as immutable CDN assets. That includes statically rendered App Router routes, Pages Router routes using getStaticProps, and dynamic routes generated with generateStaticParams. Requests for them don't invoke a Function unless Next.js needs runtime revalidation.

Routes that read request-time data run in Easel Functions. That includes routes that call cookies() or headers(), Pages Router routes using getServerSideProps, dynamic Route Handlers, API Routes, and uncached data access. Server Components and streaming responses are forwarded chunk by chunk as they render.

Partial Prerendering

Easel deploys the static shell to its cache and keeps the postponed rendering state Next.js needs. On a request, Easel can start returning the shell while the Function resumes and streams the dynamic regions. The shell and its rendering state update together when the route revalidates.

Caching and revalidation

Easel backs the Full Route Cache and Data Cache with shared storage, so cached data isn't tied to one Function instance's filesystem. With Cache Components, 'use cache' uses Next.js's in-memory cache in each Function instance, and 'use cache: remote' stores entries in Runtime Cache, shared across instances in a region. Time-based, path-based, and tag-based revalidation work through the standard Next.js APIs from any instance. See revalidation.

Attach tags to cached data:

const products = await fetch("https://api.example.com/products", {
  next: {
    revalidate: 300,
    tags: ["products"],
  },
});

Invalidate it from a Server Action or Route Handler:

"use server";

import { revalidatePath, revalidateTag } from "next/cache";

export async function updateProduct() {
  await saveProduct();

  revalidateTag("products", "max");
  revalidatePath("/products");
}

Easel coordinates the invalidation across active CDN and Function instances.

Work after the response

after() runs work once the response is sent:

import { after } from "next/server";

export async function POST(request: Request) {
  const event = await request.json();

  after(async () => {
    await recordAnalyticsEvent(event);
  });

  return Response.json({ accepted: true });
}

Easel keeps the invocation alive while registered work runs, and that time counts toward the route's duration. See background work for when to use a durable job system instead.

Proxy

Easel runs Next.js proxy, the file convention formerly called middleware, before the matching application route. Use proxy.ts and export a proxy function. A middleware.ts file still works, but Next.js 16 deprecates it. Proxy runs as a Function in US East (iad), so every request it matches makes a round trip there. Keep the matcher narrow.

proxy.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function proxy(request: NextRequest) {
  if (!request.cookies.has("session")) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*"],
};

Proxy can rewrite, redirect, check authentication, and change headers. See routing for where it sits in the request order.

Image optimization

next/image works without extra configuration. Easel's image optimization service handles the transformation requests and caches the results on the CDN. Allow remote sources in next.config.ts:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "images.example.com",
      },
    ],
  },
};

export default nextConfig;

Environment variables

Variables without the NEXT_PUBLIC_ prefix are available only during the build and in server code. Variables with NEXT_PUBLIC_ are embedded in browser bundles at build time, so changing one requires a new deployment. See environment variables for scopes and how changes apply.

OpenTelemetry

Next.js loads instrumentation.ts from the project root or src directory:

export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    await import("./instrumentation.node");
  }
}

Use standard OpenTelemetry SDKs in the Node.js module. See tracing for how Easel adds application spans to request traces.

Project configuration

SettingDefault
Install commandDetected from the package manager
Build commandPackage script or next build
Output directoryDetected from the Next.js build
Development commandPackage script or next dev
Node.js versionProject or platform default

In a monorepo, set the root directory to the folder containing the Next.js application's package.json. See monorepos.

Don't set output: "export" unless the application is intentionally static. Easel deploys the standard Next.js server output and its dynamic routes.

Set a route's maximum duration with route segment config:

export const maxDuration = 60;

Easel reads maxDuration from the build output. See function limits for the default and maximum.

Keep using next dev locally, and use a preview deployment to check production behavior.

Known limitations

  • WebSockets. Functions can't hold long-lived WebSocket connections. See Functions runtime.
  • Experimental features. Next.js experimental APIs can change their deployment contract, and Easel may need an update before every behavior works. The table above reflects the most recently tested stable release.

Troubleshooting

The project deploys as a static site

Check that next.config doesn't set output: "export", and that the build command runs next build.

A public environment variable has the old value

NEXT_PUBLIC_ variables are embedded in the browser bundle during the build. Redeploy after changing one.

A remote image is rejected

Add the image host to images.remotePatterns in next.config.

Compatibility policy

Easel tests this guide against the version in At a glance. Test new Next.js releases and experimental features in a preview deployment first. See the compatibility policy.

Next steps