Back to Blog
Tutorial10 min read2026-08-02

Shipping a Next.js Static Export Without a Server

Deploy Next.js as a static site with a one-click README button. No Node.js server needed, just pre-rendered HTML served from a CDN with sub-second load times.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

The Next.js marketing site promises server-side rendering, but your landing page doesn't need a Node.js process burning CPU for every request. A static export turns your React components into plain HTML at build time, then a CDN serves them globally with zero idle cost.

The catch: Next.js hides the static export behind a configuration flag, and forgetting output: 'export' means your build succeeds but deploys as a container instead of static files. PandaStack detects the export automatically when it's configured correctly, but you need to tell Next.js to create one first.

Configure the static export

Open next.config.js and add the output field:

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  trailingSlash: true,
  images: {
    unoptimized: true
  }
}

module.exports = nextConfig

The unoptimized: true line disables Next.js Image Optimization because it requires a server. Use standard tags or a third-party image CDN instead.

Build locally to verify it works:

npm run build

Next.js writes static files to out/. That directory becomes the deploy artifact.

Add the deploy button

Create a pandastack.json in your repository root so the deploy button knows where the built files live:

{
  "type": "static",
  "language": "nodejs",
  "buildCommand": "npm run build",
  "outputDir": "out"
}

Now add the badge to your README.md:

[![Deploy to PandaStack](https://dashboard.pandastack.io/deploy-button.svg)](https://dashboard.pandastack.io/deploy?repo=your-username/your-repo)

Replace your-username/your-repo with your GitHub repository path. When someone clicks the button, PandaStack reads pandastack.json from the main branch, pre-fills the form, and prompts them to confirm. One click later, they have a running copy of your site.

How it deploys

Push the changes to GitHub and click your own button. The deploy screen shows:

  • Type: Static
  • Build command: npm run build
  • Output directory: out

Confirm, and PandaStack clones the repo, installs dependencies with npm install, runs the build command, and uploads everything from out/ to a CDN. The entire process streams to the Logs tab in real time.

Your site goes live at https://.pandastack.app with automatic HTTPS. HTML is edge-cached, and hashed assets like _next/static/chunks/main-abc123.js are served with immutable cache headers.

Verify the output

Static exports break server-dependent features silently. Test these before you assume everything works:

  • getServerSideProps: removed at build time; pages that use it won't render
  • Dynamic routes without getStaticPaths: 404 at runtime
  • API routes: gone; a static site has no backend

If your app needs any of those, deploy it as a container instead (remove output: 'export' and let PandaStack detect the next start command). For a marketing site or documentation, the static export is faster and cheaper.

Deploy from the CLI

The deploy button is for distribution. For your own workflow, use the CLI:

panda login
panda projects create \
  --name my-nextjs-site \
  --repo your-username/your-repo \
  --branch main \
  --auto-deploy

PandaStack reads pandastack.json the same way the button does, so you don't repeat the build configuration. Redeploy on every push with --auto-deploy, or trigger manually with panda projects deploy .

What's happening under the hood

A static deploy doesn't run a pod. The build process uploads your files to object storage, then a CDN (backed by Kong ingress with Cloudflare DNS) serves them. Requests never touch Kubernetes. When you redeploy, the cache purges automatically and new content appears within seconds.

Free-tier bandwidth is 100 GB per month, which is enough for most side projects. Pro ($15/mo) bumps it to 500 GB. If you exceed the quota, the site stays online but you get billed for overage.

Common deploy button mistakes

Wrong repository format: Use owner/repo, not a full GitHub URL. https://github.com/acme/site fails; acme/site works.

Private repositories: The deploy button reads pandastack.json from GitHub's public API, which fails for private repos. Use the dashboard or CLI instead, and PandaStack authenticates with your connected GitHub account.

Missing outputDir: If pandastack.json omits outputDir, PandaStack guesses build or dist. Next.js uses out, so specify it explicitly or the deploy succeeds with an empty site.

Add environment variables

Static sites can't read server-side secrets, but build-time variables work. If your app calls an API during next build, inject the URL:

{
  "type": "static",
  "buildCommand": "npm run build",
  "outputDir": "out",
  "env": [
    {
      "key": "NEXT_PUBLIC_API_URL",
      "description": "Base URL for the backend API"
    }
  ]
}

The deploy screen prompts for NEXT_PUBLIC_API_URL before starting the build. The value becomes available as process.env.NEXT_PUBLIC_API_URL during the build, and Next.js bakes it into the client-side bundle.

References

  • [Next.js static exports](https://nextjs.org/docs/app/building-your-application/deploying/static-exports)
  • [PandaStack deploy button docs](https://docs.pandastack.io/projects/deploy-button)
  • [Static site deployment](https://docs.pandastack.io/projects/static)

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also