---
title: Asset Server
description: How Wails v3 serves and embeds your web assets in development and production
sidebar:
  order: 4
---

## Overview

Every Wails application ships a **single native executable** that combines:

1. Your *Go* backend  
2. A *Web* frontend (HTML + JS + CSS)

The **Asset Server** is the glue that makes this possible.  
It has **two operating modes** that are selected at compile-time via Go build
tags:

| Mode | Tag | Purpose |
|------|-----|---------|
| **Development** | `//go:build dev` | Fast iteration with hot-reload |
| **Production**  | `//go:build !dev` | Zero-dependency, embedded assets |

The implementation lives in  
`v3/internal/assetserver/` with clear file splits:

```
assetserver_dev.go        # ⬅️ runtime dev server
assetserver_production.go # ⬅️ embedded server
assetserver_darwin.go     # OS-specific helpers (same for linux/windows)
asset_fileserver.go       # Shared static file logic
content_type_sniffer.go   # MIME type detection
ringqueue.go              # Tiny LRU for mime cache
```

---

## Development Mode

### Lifecycle

1. `wails3 dev` boots and **spawns your frontend dev server**  
   (Vite, SvelteKit, React-SWC …) by running the task defined in
   `build/Taskfile.yml` (usually `npm run dev`).
2. Wails starts the **Dev Asset Server** listening on `localhost:<random>` and
   tells the Go runtime to load `http://<host>:<VITE_PORT>` as the window URL.
3. Incoming requests are handled by `assetserver_dev.go`:

   ```
   ┌─────────┐  /runtime/...          ┌─────────────┐
   │ Browser │ ── native bridge  ───▶ │   Runtime   │
   ├─────────┤                       └─────────────┘
   │   JS    │  / (index.html)        proxy / -> Vite
   └─────────┘ ◀─────────────┐
                 AssetServer │
                             ▼
                      ┌────────────┐
                      │  Vite Dev  │
                      │   Server   │
                      └────────────┘
   ```

4. Static files (`/assets/logo.svg`) are **served directly from disk** via
   `asset_fileserver.go` (for speed) while anything unknown is **proxied** to
   the framework dev server, giving you *instant* hot-module replacement.

### Features

* **Live Reload** – Vite/Snowpack/… injects HMR WebSocket; Wails only has to
  proxy it.
* **Source Map Support** – because assets are not bundled, your browser devtools
  map errors back to original source.
* **No Go Re-compile** – Only the frontend rebuilds; Go code stays running until
  you change `.go` files.

### Switching Frameworks

The dev proxy is **framework-agnostic**:

* The root `Taskfile.yml` task file injects two env vars:
  `APP_NAME` & `WAILS_VITE_PORT`.
* Taskfiles for each template emit those vars before running their dev servers.
* `assetserver_dev.go` simply proxies to that target.

Add a new template → define its dev task → Asset Server just works.

---

## Production Mode

When you run `wails3 build` the pipeline:

1. Runs the frontend **production build** (`npm run build`) producing
   `/frontend/dist/**`.
2. **Embeds** that folder into `go:embed` FS at compile time (see
   `bundled_assetserver.go` generated file).
3. Compiles the Go binary with `-tags production` (implicit).

### Request Handling

```go
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 1. Try embedded static assets (exact path)
    // 2. Fallback to index.html for SPA routing
    // 3. Sniff content-type if extension unknown
    // 4. Set strong cache headers
}
```

* **MIME Detection** – If the build tool produced extension-less files (e.g.
  `/assets/manifest`) `content_type_sniffer.go` inspects the first 512 bytes and
  caches the result in a tiny lock-free LRU.
* **Ring Queue Caching** – Frequently accessed assets (logo, CSS) are kept
  in-memory for the lifetime of the app, removing the need for disk or embed FS
  lookups.
* **Security Headers** – Disallows `file://` navigation, enables `nosniff`.

Because everything is embedded, the shipped binary has **no external
dependencies** (even on Windows).

---

## Bridging Dev ↔ Prod

Both modes expose the **same public interface**:

```go
type AssetServer interface {
    URL()        string // dev: http://localhost:34115, prod: wails://app
    Open() error // start listening
    Close()      // graceful shutdown
}
```

`pkg/application` happily uses whichever implementation was compiled in, meaning
**your application code does not change** between `dev` and `build`.

---

## How Frontend Frameworks Integrate

### Templates

Each official template (React, Vue, Svelte, Solid…) contains:

* `build/Taskfile.yml`
* `frontend/vite.config.ts` (or equivalent)

They export several tasks including:

| Task | Purpose |
|------|---------|
| `dev:frontend`   | Starts the framework dev server on a **random free port** and prints it to stdout (`PORT=5173`). |
| `build:frontend` | Produces static assets into `dist/` + manifest for cache-busting. |

`internal/commands/dev.go` sets the `FRONTEND_DEVSERVER_URL` env var using the hard-coded `localhost` as the `host` and the emitted `VITE_PORT` env var as the `port` and launches the **Dev Asset Server**.

Frameworks remain fully decoupled from Go:

* No need to import Wails JS SDK at build time – the runtime injects it at
  window creation.
* Any framework with an HTTP dev server can plug in.

---

## Extending / Customising

Need custom headers, auth, or gzip?

1. Implement `type Middleware func(http.Handler) http.Handler`
2. Register via `internal/assetserver/options.go`
3. For prod, remember to add the same middleware in `assetserver_production.go`.

---

## Key Source Files

| File | Role |
|------|------|
| `assetserver_dev.go` | Reverse proxy + disk file server |
| `assetserver_production.go` | Embedded FS handler |
| `options.go` | Config struct parsed from `pkg/options/assetserver` |
| `build_dev.go` / `build_production.go` | Build-tag wrappers selecting correct implementation |
| `bundled_assetserver.go` | Generated embed data (only present in production builds) |

---

## Gotchas & Debugging

* **White Screen in Prod** – usually SPA routing: ensure `History API Fallback`
  is enabled in dev and `index.html` fallback works in prod.
* **404 in Dev** – mis-matched `FRONTEND_DEV_PORT`; run with
  `WAILSDEV_VERBOSE=1` to print every proxied request.
* **Large Assets** – they are embedded; consider
  [`assetserver.WithExternalDir("/path")`](https://pkg.go.dev) to load from disk.

---

You now know how the Wails **Asset Server** feeds your web code to the native
window in both **development** and **production**.  
Master this layer and you can debug loading issues, add middlewares, or even
swap in a completely different frontend tool-chain with confidence.
