Skip to main content
Version: v2.8.0

Dynamic Assets

If you want to load or generate assets for your frontend dynamically, you can achieve that using the AssetsHandler option. The AssetsHandler is a generic http.Handler which will be called for any non GET request on the assets server and for GET requests which can not be served from the bundled assets because the file is not found.

By installing a custom AssetsHandler, you can serve your own assets using a custom asset server.


In our example project, we will create a simple assets handler which will load files off disk:

package main

import (

//go:embed all:frontend/dist
var assets embed.FS

type FileLoader struct {

func NewFileLoader() *FileLoader {
return &FileLoader{}

func (h *FileLoader) ServeHTTP(res http.ResponseWriter, req *http.Request) {
var err error
requestedFilename := strings.TrimPrefix(req.URL.Path, "/")
println("Requesting file:", requestedFilename)
fileData, err := os.ReadFile(requestedFilename)
if err != nil {
res.Write([]byte(fmt.Sprintf("Could not load file %s", requestedFilename)))


func main() {
// Create an instance of the app structure
app := NewApp()

// Create application with options
err := wails.Run(&options.App{
Title: "helloworld",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
Handler: NewFileLoader(),
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 255},
OnStartup: app.startup,
Bind: []interface{}{

if err != nil {
println("Error:", err)

When we run the application in dev mode using wails dev, we will see the following output:

DEB | [ExternalAssetHandler] Loading 'http://localhost:3001/favicon.ico'
DEB | [ExternalAssetHandler] Loading 'http://localhost:3001/favicon.ico' failed, using AssetHandler
Requesting file: favicon.ico

As you can see, the assets handler is called when the default assets server is unable to serve the favicon.ico file.

If you right click the main application and select "inspect" to bring up the devtools, you can test this feature out by typing the following into the console:

let response = await fetch('does-not-exist.txt');

This will generate an error in the devtools. We can see that the error is what we expect, returned by our custom assets handler:

However, if we request go.mod, we will see the following output:

This technique can be used to load images directly into the page. If we updated our default vanilla template and replaced the logo image:

<img id="logo" class="logo" />


<img src="build/appicon.png" style="width: 300px" />

Then we would see the following:


Exposing your filesystem in this way is a security risk. It is recommended that you properly manage access to your filesystem.