Back to Blog
Tutorial11 min read2026-08-02

Deploy a Nuxt Static Export with the API and a README Button

Ship a Nuxt static site in three ways — REST API from CI, a one-click deploy button, and the dashboard. Zero server runtime, full CDN caching, instant cache purges on redeploy.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Nuxt 3 static generation renders your entire site at build time and outputs plain HTML, CSS, and JavaScript. No Node.js server in production, no cold starts, just files served from a CDN with edge caching and automatic SSL. The deployment flow is straightforward until you try to automate it — most platforms make you click through a dashboard form every time, and reproducing the exact build settings in CI requires guessing query parameters.

This guide shows you three deploy paths for the same Nuxt static site: a one-click button for your README, a REST API call for CI pipelines, and the dashboard flow for quick manual deploys. All three produce identical results; pick the one that fits your workflow.

The Nuxt static site

Create a new Nuxt 3 project with static output:

npx nuxi@latest init nuxt-static-demo
cd nuxt-static-demo

Edit nuxt.config.ts to enable static generation:

export default defineNuxtConfig({
  ssr: false,
  generate: {
    routes: ['/']
  }
})

The ssr: false setting tells Nuxt to pre-render pages as static HTML instead of running a server. Add a page to verify it works:

<!-- pages/index.vue -->
<template>
  <div>
    <h1>Nuxt Static Demo</h1>
    <p>Built at: {{ buildTime }}</p>
    <NuxtLink to="/about">About</NuxtLink>
  </div>
</template>

<script setup>
const buildTime = new Date().toISOString()
</script>
<!-- pages/about.vue -->
<template>
  <div>
    <h1>About</h1>
    <NuxtLink to="/">Home</NuxtLink>
  </div>
</template>

Build it:

npm run generate

Nuxt writes static files to .output/public/. That directory is what gets deployed — no .output/server/, no Nitro runtime, just a folder of HTML and hashed assets. Test it locally:

npx serve .output/public

Visit http://localhost:3000. Both routes should work, the build timestamp should be frozen (it ran at build time, not request time), and the browser's network tab should show hashed filenames like _nuxt/index.abc123.js. Push the project to GitHub.

Deploy with the REST API (for CI)

Generate a PandaStack API token from the dashboard Settings page. Copy the psk_live_xxx token — it has your organization baked in, so no extra headers are needed. Create the project via the API:

curl -X POST https://api.pandastack.io/v1/projects \
  -H "Authorization: Bearer psk_live_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "static",
    "name": "nuxt-static-demo",
    "repositoryName": "yourname/nuxt-static-demo",
    "branch": "main",
    "buildCommand": "npm run generate",
    "outputDir": ".output/public",
    "autoDeploy": true
  }'

The response includes projectId and deploymentId. The build starts immediately. Check the logs:

curl -H "Authorization: Bearer psk_live_your_token_here" \
  https://api.pandastack.io/v1/projects/{projectId}/deployments/{deploymentId}/logs

You'll see npm install, nuxt generate, and a final message with the live URL. Open it in a browser — the site is live, served from a CDN, with HTTPS automatically enabled. The build output directory (.output/public) becomes the CDN root, so /index.html is served at /, and hashed assets load from /_nuxt/.

To redeploy after a push, call:

curl -X POST https://api.pandastack.io/v1/projects/{projectId}/deploy \
  -H "Authorization: Bearer psk_live_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"branch": "main"}'

This is the same flow GitHub Actions would use. Store the token as a secret, add a workflow that runs on push to main, and your site rebuilds automatically.

Add a deploy button to your README

For open-source projects or starter templates, a one-click button removes all setup friction. Someone forks your repo, clicks the button, and gets a live copy without touching the command line.

In your README.md:

[![Deploy to PandaStack](https://dashboard.pandastack.io/deploy-button.svg)](https://dashboard.pandastack.io/deploy?repo=yourname/nuxt-static-demo&type=static&buildCmd=npm%20run%20generate&outputDir=.output/public)

The query parameters seed the deploy form:

  • repo (required): yourname/nuxt-static-demo
  • type: static
  • buildCmd: npm run generate (URL-encoded)
  • outputDir: .output/public

Clicking the button opens the deploy page with those fields pre-filled. The user confirms and hits Deploy. PandaStack clones the repo, runs the build, and publishes the static output to a CDN URL.

If your repo includes a pandastack.json file, its values override the query params:

{
  "type": "static",
  "name": "nuxt-static-demo",
  "buildCommand": "npm run generate",
  "outputDir": ".output/public"
}

Commit that to the repo root, and the button URL simplifies to:

[![Deploy to PandaStack](https://dashboard.pandastack.io/deploy-button.svg)](https://dashboard.pandastack.io/deploy?repo=yourname/nuxt-static-demo)

The config file is read from the main branch via GitHub's public contents API, base64-decoded, and applied. This is Vercel-compatible — a repo with a Vercel button works with just a domain swap.

Manual deploy from the dashboard

Go to https://dashboard.pandastack.io, click New Project, select your GitHub repo, and choose Static Site. The dashboard auto-detects Nuxt and fills in:

  • Build command: npm run generate
  • Output directory: .output/public

Click Deploy. The same build runs, the same static files go to the CDN, and the same URL format appears. This is the fastest path for one-off deploys, but it doesn't scale to teams or CI.

How static deploys differ from container deploys

Static sites on PandaStack are served from a CDN with no running pods. There's no idle cost — you pay nothing when the site isn't being accessed. The CDN caches HTML at the edge for a short TTL (since you redeploy often), and hashed assets (./_nuxt/index.abc123.js) are cached indefinitely with immutable headers.

When you redeploy, PandaStack purges the CDN cache automatically. The new build's hashed filenames differ from the old ones, so browsers never load stale JavaScript. The HTML cache expires within seconds, so users see the new version immediately.

Container apps work differently: a pod runs your app's server process, scales to zero when idle (free tier), and cold-starts on the next request. Static sites skip that entirely — there's no process, no port binding, no health checks. Just files and HTTP headers.

What breaks and how to fix it

404 on /about after deploy: Nuxt didn't pre-render the route. Either add it to generate.routes or enable ssr: true with nitro.preset: 'static' to crawl links automatically. The default Nuxt 3 static config only renders routes you explicitly list.

API calls fail in production: You built a static site but your code calls useFetch('/api/data'). Static output has no server API routes — they're stripped at build time. Move the API calls to build-time data fetching (useAsyncData in a page with ssr: true during generate), or deploy a separate container app for the API and call it from the client.

Hashed asset URLs break: You referenced an image with /images/logo.png instead of ~/assets/logo.png. Only assets imported via ~/assets/ or ~/public/ get the correct Nuxt base path handling. Files in public/ are copied as-is and served from the CDN root.

Environment variables missing at runtime: Static sites baked the env vars into the JavaScript at build time via Nuxt's runtimeConfig.public. If you change an env var after deploy, you must rebuild — there's no server process to reload them.

Choosing between static and SSR

Nuxt can generate static HTML (this guide) or run a Node.js server (SSR/hybrid rendering). Static is faster, cheaper, and simpler for content sites, marketing pages, and documentation. SSR is required for:

  • Per-request data fetching (user-specific dashboards, auth-protected pages)
  • Server API routes (/api/*)
  • On-demand revalidation (rebuild a single page without redeploying everything)

If you need SSR, change nuxt.config.ts to ssr: true and deploy as a container app instead of static. PandaStack's auto-detection picks the right type based on the outputDir — if it's .output/public, it's static; if it's .output/server, it's a container.

Next steps

You've deployed a Nuxt static site with three different methods. The API call works in CI, the deploy button works in READMEs, and the dashboard works for quick manual deploys. All three read from the same pandastack.json config, so your build settings stay consistent.

For production sites, add a custom domain in the project settings, and PandaStack provisions SSL automatically. For larger static sites (Astro content collections with hundreds of pages, VitePress docs), the build process is identical — just point outputDir at the framework's output folder.

References

  • [Nuxt static generation](https://nuxt.com/docs/getting-started/deployment#static-hosting)
  • [PandaStack deploy button](https://docs.pandastack.io/projects/deploy-button)
  • [PandaStack API reference](https://docs.pandastack.io/api)
  • [Custom domains guide](https://docs.pandastack.io/projects/custom-domains)

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also