Skip to main content
Version: v2.0.0-beta.38

Application Development

There are no hard and fast rules for developing applications with Wails, but there are some basic guidelines.

Application Setup​

The pattern used by the default templates are that main.go is used for configuring and running the application, whilst app.go is used for defining the application logic.

The app.go file will define a struct that has 2 methods which act as hooks into the main application:

app.go
type App struct {
ctx context.Context
}

func NewApp() *App {
return &App{}
}

func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}

func (a *App) shutdown(ctx context.Context) {
}
  • The startup method is called as soon as Wails allocates the resources it needs and is a good place for creating resources, setting up event listeners and anything else the application needs at startup. It is given a context.Context which is usually saved in a struct field. This context is needed for calling the runtime. If this method returns an error, the application will terminate. In dev mode, the error will be output to the console.

  • The shutdown method will be called by Wails right at the end of the shutdown process. This is a good place to deallocate memory and perform any shutdown tasks.

The main.go file generally consists of a single call to wails.Run(), which accepts the application configuration. The pattern used by the templates is that before the call to wails.Run(), an instance of the struct we defined in app.go is created and saved in a variable called app. This configuration is where we add our callbacks:

main.go
func main() {

app := NewApp()

err := wails.Run(&options.App{
Title: "My App",
Width: 800,
Height: 600,
OnStartup: app.startup,
OnShutdown: app.shutdown,
})
if err != nil {
log.Fatal(err)
}
}

More information on application lifecycle hooks can be found here.

Binding Methods​

It is likely that you will want to call Go methods from the frontend. This is normally done by adding public methods to the already defined struct in app.go:

app.go
type App struct {
ctx context.Context
}

func NewApp() *App {
return &App{}
}

func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}

func (a *App) shutdown(ctx context.Context) {
}

func (a *App) Greet(name string) string {
return fmt.Printf("Hello %s!", name)
}

In the main application configuration, the Bind key is where we can tell Wails what we want to bind:

main.go
func main() {

app := NewApp()

err := wails.Run(&options.App{
Title: "My App",
Width: 800,
Height: 600,
OnStartup: app.startup,
OnShutdown: app.shutdown,
Bind: []interface{}{
app,
},
})
if err != nil {
log.Fatal(err)
}
}

This will bind all public methods in our App struct (it will never bind the startup and shutdown methods).

More information on Binding can be found here.

Application Menu​

Wails supports adding a menu to your application. This is done by passing a Menu struct to application config. It's common to use a method that returns a Menu, and even more common for that to be a method on the App struct used for the lifecycle hooks.

main.go
func main() {

app := NewApp()

err := wails.Run(&options.App{
Title: "My App",
Width: 800,
Height: 600,
OnStartup: app.startup,
OnShutdown: app.shutdown,
Menu: app.menu(),
Bind: []interface{}{
app,
},
})
if err != nil {
log.Fatal(err)
}
}

Assets​

The great thing about the way Wails v2 handles assets is that it doesn't! The only thing you need to give Wails is an embed.FS. How you get to that is entirely up to you. You can use vanilla html/css/js files like the vanilla template. You could have some complicated build system, it doesn't matter.

When wails build is run, it will check the wails.json project file at the project root. There are 2 keys in the project file that are read:

  • "frontend:install"
  • "frontend:build"

The first, if given, will be executed in the frontend directory to install the node modules. The second, if given, will be executed in the frontend directory to build the frontend project.

If these 2 keys aren't given, then Wails does absolutely nothing with the frontend. It is only expecting that embed.FS.

AssetsHandler​

A Wails v2 app can optionally define a http.Handler in the options.App, which allows hooking into the AssetServer to create files on the fly or process POST/PUT requests. GET requests are always first handled by the assets FS. If the FS doesn't find the requested file the request will be forwarded to the http.Handler for serving. Any requests other than GET will be directly processed by the AssetsHandler if specified. It's also possible to only use the AssetsHandler by specifiy nil as the Assets option.

Built in Dev Server​

Running wails dev will start the built in dev server which will start a file watcher in your project directory. By default, if any file changes, wails checks if it was an application file (default: .go, configurable with -e flag). If it was, then it will rebuild your application and relaunch it. If the changed file was in the assets, it will issue a reload after a short amount of time.

The dev server uses a technique called "debouncing" which means it doesn't reload straight away, as there may be multiple files changed in a short amount of time. When a trigger occurs, it waits for a set amount of time before issuing a reload. If another trigger happens, it resets to the wait time again. By default this value is 100ms. If this value doesn't work for your project, it can be configured using the -debounce flag. If used, this value will be saved to your project config and become the default.

External Dev Server​

Some frameworks come with their own live-reloading server, however they will not be able to take advantage of the Wails Go bindings. In this scenario, it is best to run a watcher script that rebuilds the project into the build directory, which Wails will be watching. For an example, see the default svelte template that uses rollup. For create-react-app, it's possible to use this script to achieve a similar result.

Go Module​

The default Wails templates generate a go.mod file that contains the module name "changeme". You should change this to something more appropriate after project generation.