Skip to content
Astro as project 4 min read

Configuring astro.config.mjs

Every Astro project is steered by a single configuration file at the root: astro.config.mjs. This is where you declare your production URL, register integrations, choose how pages are rendered, and tune the underlying Vite build. Because Astro ships zero JavaScript to the browser by default, most of what you configure here shapes the build rather than the runtime — and getting it right unlocks correct canonical URLs, sitemaps, server rendering, and a smooth developer experience.

The defineConfig helper

The file exports a default config object. While you can export a plain object, you should always wrap it in defineConfig. The helper adds full TypeScript IntelliSense and validation even in a .mjs file, so your editor autocompletes every option and flags typos before you run the build.

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  site: 'https://devcraftly.com',
  base: '/',
  output: 'static',
});

The file extension matters. .mjs is the most common choice because it guarantees ES module syntax regardless of your package.json settings. You may also use astro.config.ts, astro.config.js, or astro.config.mts — Astro resolves them in that priority order.

Tip: Run astro check or simply hover over any option in your editor. Because defineConfig is fully typed, you get inline docs and accepted values without leaving the file.

Setting site and base

The site option is the absolute, canonical URL of your deployed project. Astro uses it to generate correct absolute links, RSS feed URLs, canonical tags, and sitemaps. Omitting it silently breaks the @astrojs/sitemap integration and any code that reads Astro.site.

The base option is the path your site is served from. If you deploy to https://example.com/docs/, set base: '/docs' so all internal links and asset URLs are prefixed correctly.

import { defineConfig } from 'astro/config';

export default defineConfig({
  site: 'https://devcraftly.com',
  base: '/docs',
});
OptionTypePurpose
sitestring (absolute URL)Canonical origin for links, sitemaps, RSS
basestring (path)Sub-path the site is deployed under
trailingSlash'always' | 'never' | 'ignore'URL slash policy for routing

Choosing an output mode

The output option controls how pages are rendered. The default, 'static', prebuilds every page to HTML at build time — ideal for content sites and the source of Astro’s zero-JS-by-default speed. Use 'server' when you need on-demand rendering (SSR) for pages that depend on requests, cookies, or live data. In server mode you opt individual pages back into static prerendering with export const prerender = true.

Server output requires an adapter that targets your host. The adapter is supplied via the adapter option, usually added with the CLI.

npx astro add node
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

export default defineConfig({
  output: 'server',
  adapter: node({ mode: 'standalone' }),
});
outputBehavior
'static'All routes prerendered to HTML at build time (default)
'server'Routes rendered on demand; opt into static per-page with prerender

Warning: In output: 'server' mode you must install an adapter (@astrojs/node, @astrojs/vercel, @astrojs/cloudflare, etc.). Building SSR without one throws an error.

Registering integrations

Integrations extend Astro with framework renderers, Markdown tooling, and build-time features. They live in the integrations array. The astro add command installs the package and edits this array for you, but you can also add entries by hand.

npx astro add react sitemap mdx
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import sitemap from '@astrojs/sitemap';
import mdx from '@astrojs/mdx';

export default defineConfig({
  site: 'https://devcraftly.com',
  integrations: [react(), mdx(), sitemap()],
});

Once @astrojs/react is registered, you can render React components as islands. They are static HTML until you add a client:* directive, which is exactly how Astro keeps the JavaScript footprint minimal.

---
import Counter from '../components/Counter.tsx';
---
<!-- Renders to static HTML, ships zero JS -->
<Counter />

<!-- Hydrates in the browser only when visible -->
<Counter client:visible />

Tuning the Vite build

Astro is built on Vite, and the vite key passes options straight through to it. Use it to define aliases, set up SSR externalization, add Vite plugins, or expose constants. Anything valid in a Vite config is valid here.

import { defineConfig } from 'astro/config';

export default defineConfig({
  vite: {
    resolve: {
      alias: {
        '@components': '/src/components',
      },
    },
    server: {
      port: 4321,
    },
    define: {
      __APP_VERSION__: JSON.stringify('1.0.0'),
    },
  },
});

Running the dev server confirms your settings:

npm run dev

Output:

  astro  v5.0.0 ready in 312 ms

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

Best Practices

  • Always wrap your config in defineConfig for type-checked autocompletion.
  • Set site to your real production URL early — sitemaps, RSS, and canonical links depend on it.
  • Prefer output: 'static' and opt specific pages into SSR rather than making the whole site server-rendered.
  • Use npx astro add <name> to register integrations so the package install and config edit stay in sync.
  • Keep base and trailingSlash consistent with your host to avoid broken internal links.
  • Reach for the vite key only when an Astro-level option does not exist; most needs are covered by top-level config.
Last updated June 14, 2026
Was this helpful?