Skip to content
Astro as getting-started 4 min read

The Dev Server

The development server is where you’ll spend most of your time building an Astro site. It compiles your .astro components, content collections, and integrations on demand, ships only the JavaScript your islands actually need, and reloads the browser the instant you save a file. Because it’s powered by Vite, startup is near-instant and updates feel live rather than batched. This page covers how to start the server, how hot module reloading behaves with Astro’s zero-JS-by-default model, and the flags that make local development smoother.

Starting the server

Astro projects scaffold a dev script in package.json, so the canonical way to launch the server is through your package manager.

npm run dev

Under the hood this runs the astro dev command. You can also invoke it directly, which is handy when you want to pass flags ad hoc.

npx astro dev

By default the server listens on http://localhost:4321. Astro prints the URL, the time it took to boot, and the active integrations once it’s ready.

Output:

 astro  v5.4.0 ready in 312 ms

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

watching for file changes...

Hot module reloading

Hot Module Replacement (HMR) swaps changed modules into the running page without a full reload, preserving in-page state where possible. Astro’s behavior depends on what you edit:

  • Editing the markup or component script (--- frontmatter) of a .astro file triggers a fast re-render of that route.
  • Editing a framework component (React, Svelte, Vue, etc.) that’s hydrated as an island updates the island in place, keeping its client state intact thanks to the framework’s own HMR.
  • Editing styles — whether in a <style> block or an imported CSS file — patches the page with no reload at all.
---
// src/components/Counter.tsx is a hydrated island
import Counter from "../components/Counter.tsx";
---

<h1>Live editing</h1>
<Counter client:load />

Because Astro renders most output to static HTML and only hydrates the islands you mark with a client:* directive, server-rendered markup updates re-run on the server and stream fresh HTML, while island state is handled by the framework’s HMR runtime. The result is that you get instant feedback without shipping a heavyweight client bundle.

Tip: If HMR ever gets into a confused state (rare, usually after large config changes), a manual browser refresh resolves it. Changes to astro.config.mjs require a full server restart, which Astro will prompt you to do.

The error overlay

When your code throws during development, Astro renders a full-screen error overlay directly in the browser. It shows the error message, the file and line number, a code frame highlighting the offending line, and a stack trace. This applies to compile errors in .astro files, runtime errors in your component scripts, and errors thrown while rendering content collections.

---
const post = await getEntry("blog", "missing-slug");
const title = post.data.title; // throws: Cannot read properties of undefined
---

The overlay updates live — fix the error and save, and it disappears as the page re-renders. You never need to dig through the terminal to find a stack trace, though the same error is logged there too.

Network access and dev flags

By default the server binds to localhost only, so other devices on your network can’t reach it. Pass --host to expose it — useful for testing on a phone or tablet. You can also override the port and open a browser automatically.

# Expose on the local network and pick a custom port
npx astro dev --host --port 3000

# Open the default browser on startup
npx astro dev --open

The most commonly used development flags:

FlagDescription
--hostBind to all network interfaces so other devices can connect
--host <address>Bind to a specific address
--port <number>Listen on a custom port (default 4321)
--openOpen the site in your default browser on startup
--config <path>Use a config file other than astro.config.mjs
--root <path>Run against a project root other than the current directory
--verboseEnable verbose logging for debugging
--silentSuppress all logging output

You can also configure the server permanently in astro.config.mjs instead of passing flags every time.

import { defineConfig } from "astro/config";

export default defineConfig({
  server: {
    port: 3000,
    host: true, // equivalent to --host
  },
});

Warning: Exposing the dev server with --host makes it reachable by anyone on your network. The dev server is not hardened for production — never deploy it. Use astro build and a real adapter or static host for production traffic.

Best practices

  • Run the server through your dev script so everyone on the team uses the same configuration and flags.
  • Keep astro.config.mjs changes deliberate — they require a restart, so batch config edits rather than tweaking one line at a time.
  • Lean on the error overlay: read the code frame before opening the terminal, since it points you straight to the failing line.
  • Use --host for cross-device testing, but turn it off on shared or public networks.
  • Hydrate islands with the lightest directive that works (client:visible over client:load) so HMR stays fast and bundles stay small.
  • Never serve the dev server as your production site; always run astro build and deploy the output.
Last updated June 14, 2026
Was this helpful?