Skip to content
Astro as deployment 4 min read

Building for Production

When you are ready to ship, astro build compiles your project into a set of optimized, static assets that any host can serve. Astro’s defining principle—zero JavaScript by default—shows up most clearly here: the build renders every component to HTML ahead of time and only ships client-side JavaScript for the islands you explicitly hydrate. This page walks through what the build emits, how to control the output directory, and how to preview the result locally so you catch problems before they reach production.

Running the build

The production build is a single command. From your project root, run:

npm run build

This invokes astro build (defined in your package.json scripts). Astro renders all pages, bundles and minifies your CSS and JavaScript, optimizes images, and writes everything to the output directory.

Output:

astro v5.4.0 building static entrypoints...
✓ Completed in 842ms.

building client (vite)
✓ 6 modules transformed.
dist/_astro/Counter.D8kQv2.js   1.18 kB │ gzip: 0.67 kB
✓ built in 311ms

generating static routes
▶ /index.html                  (+12ms)
▶ /about/index.html            (+4ms)
▶ /blog/[slug]/index.html
  ├─ /blog/hello-world/index.html (+9ms)
  └─ /blog/astro-tips/index.html  (+3ms)
✓ Completed in 38ms.

Complete!

Notice that only the interactive Counter island produced a JavaScript bundle. Static pages with no client:* directives ship pure HTML and CSS.

What the build emits

By default the build writes to a dist/ folder at your project root. The exact structure depends on your rendering mode.

Output modeWhat is generatedWhere it runs
static (default)Pre-rendered .html files plus hashed assetsAny static file host or CDN
serverA server entry point plus pre-rendered static pagesA Node, edge, or serverless adapter

A typical static build produces:

dist/
├── _astro/            # hashed, minified JS and CSS for islands
├── index.html
├── about/
│   └── index.html
└── blog/
    └── hello-world/
        └── index.html

The _astro/ directory holds fingerprinted (content-hashed) asset files, so you can cache them aggressively—changing a file changes its filename, busting the cache automatically.

Configuring the output directory

You can change where the build writes its files with the outDir option in astro.config.mjs. The build.assets option controls the name of the hashed-assets subfolder.

import { defineConfig } from "astro/config";

export default defineConfig({
  outDir: "./build",
  build: {
    assets: "static",
    // "directory" -> /page/index.html (default), "file" -> /page.html
    format: "directory",
  },
});

The build.format option matters for how URLs resolve on your host. directory produces /about/index.html (clean URLs like /about/), while file produces /about.html. Choose whichever matches your host’s routing conventions.

Astro empties the output directory at the start of every build. Never put source files you care about in dist/ (or your custom outDir)—they will be deleted.

Previewing the build locally

The dev server (astro dev) uses on-demand compilation and is not representative of production. To inspect the actual built output, use the preview server:

npm run build
npm run preview

Output:

┃ Local    http://localhost:4321/
┃ Network  use --host to expose

watching for file changes...

astro preview serves the contents of dist/ over HTTP exactly as a static host would, which lets you verify asset paths, redirects, and hydration before deploying.

astro preview only serves a static build out of the box. If you use an SSR adapter, preview behavior depends on the adapter—some (like Node) support it, while serverless adapters do not. Check your adapter’s docs.

Setting the site and base

For correct canonical URLs, sitemaps, and absolute asset links, set site to your production domain. If you deploy under a subpath, also set base.

import { defineConfig } from "astro/config";

export default defineConfig({
  site: "https://example.com",
  base: "/docs", // only if the app lives at example.com/docs
});

When base is set, reference internal links and assets relative to it using import.meta.env.BASE_URL:

---
const base = import.meta.env.BASE_URL;
---
<a href={`${base}/about`}>About</a>

Best practices

  • Always set site (and base when deploying to a subpath) so generated URLs and sitemaps are correct.
  • Preview every build with astro preview before deploying; the dev server can hide path and hydration issues.
  • Keep islands minimal—add client:* directives only where interactivity is required to preserve the zero-JS-by-default baseline.
  • Rely on content-hashed filenames in _astro/ to set long-lived cache headers on assets safely.
  • Treat the output directory as disposable; commit only source, and let CI run astro build fresh each time.
  • Run astro check in CI alongside the build to catch type and content-collection errors before shipping.
Last updated June 14, 2026
Was this helpful?