Skip to content

Deploy Next.js

deploybase serves static files, so Next.js has to run in static export mode. That is one line of config, plus a decision about any feature in your app that expects a server.

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
};
module.exports = nextConfig;

next build then writes a complete site to out/ instead of starting a server. Next.js 13.3 and later support this; on older versions you need next export as a separate step.

Setting Value
Build command npm run build
Output directory out
Image node:24-alpine

Every value above is editable per project, and the full defaults table covers the other frameworks.

Next.js fails the build for most of these rather than deploying something subtly broken, which is the behaviour you want:

Feature Status What to do instead
API routes, route handlers Not available Call an API from the browser, or use Forms for submissions
Middleware Not available Redirects and rewrites at the edge, or client-side routing
getServerSideProps Not available getStaticProps, fetching at build time
Incremental static regeneration Not available Redeploy when content changes. A webhook from your CMS to the deploy API rebuilds in seconds
Server Components doing runtime work Build time only They run during the build, so their data is frozen into the HTML
next/image default loader Needs config See below
Draft mode, cookies(), headers() Not available Nothing is running per request to read them

If your app depends on several of these, static export is the wrong shape for it, and deploybase is the wrong host for it. Better to know that before a migration than after one.

The default image loader optimises on demand, which needs a server:

const nextConfig = {
output: 'export',
images: { unoptimized: true },
};

unoptimized: true ships your images as they are, so size them before committing them. Alternatively point images.loader at an external service, which keeps next/image responsive srcsets while the transformation happens elsewhere.

A static export writes about.html by default. Most static hosts, ours included, serve /about from about/index.html:

const nextConfig = {
output: 'export',
trailingSlash: true,
};

This is worth settling before the site has traffic, because changing it later changes every URL on the site and costs you the search rankings attached to them.

Only NEXT_PUBLIC_ variables reach the browser

Section titled “Only NEXT_PUBLIC_ variables reach the browser”

Environment variables are injected during the build. Next.js inlines the NEXT_PUBLIC_-prefixed ones into the client bundle and keeps the rest to build-time code. Because there is no server, a non-public variable is readable only by getStaticProps and friends while the build runs.

A NEXT_PUBLIC_ value is published in your JavaScript. Treat it as public.

Page "/blog/[slug]" is missing "generateStaticParams()". A dynamic route has no list of pages to write. Export generateStaticParams (App Router) or getStaticPaths (Pages Router) returning every slug.

export const dynamic = "force-dynamic" errors at build. That route is asking for per-request rendering. Remove the directive, or move the work into the browser.

The deploy is empty, or 404s everywhere. output: 'export' is missing, so next build produced .next/ rather than out/. Add it and redeploy.

Images 404 in production but work in next dev. The default loader is still configured. Set images.unoptimized.

Routes work on the first load but 404 on refresh. Trailing-slash handling does not match how the files were written. Set trailingSlash: true and redeploy.

Live a8b7eb1d built