Go is mainly used for backend services and command line tools. But sometimes a more visual user interface is required. I went on a journey to find the right UI framework for use with Go.
Scope of this post
This article analyses and compares different UI frameworks or framework-bindings for Go. It is not about web-based frameworks, because focus is on usability and performance.
Is there a thing called “Native”?
When evaluating frameworks for specific programming languages, an important point to consider is whether the framework or at least its interface is “natively” written in that specific language. Usually, this means that the framework is easier to use and can be integrated in a consistent way, which (hopefully) leads to simple and readable code.
The most natural thing would be to use a standard “native” UI framework. But as of version 1.18.4, Go does not offer a UI framework in its standard library.
Third party Go UI frameworks are based on OS specific APIs or OpenGL to render the UI. Since neither OpenGL nor any OS known to me offer native Golang interfaces, in the end, there are always C calls to C-based libraries. Actually, while there is a possibility to build “native” shared libraries in Go, this is kind of against the core value of statically linked binaries and therefore kind of neglected.
Even though a very formal judgement may be that “there are no native UI libraries for Go”, this would not be helpful. We are looking for a framework that “feels” native, offers good performance and can easily be used.
Rendering based on OS UI
Walk is short for “Windows Application Library Toolkit” and basically wraps Windows UI API. It is actually quite simple to use with Go if you have used Windows API before, and the resulting programs load extremely fast due to no additional layers. Walk can be very useful for small and simple UIs, if you are sure that they will ever only be run on Windows. Other than that, don’t use it.
go-gtk implements Go bindings for the popular GTK framework. The bindings are manually maintained and incomplete, which kind of reduces their usefullness. Even though the bindings are not OS specific - they will work on all operating systems which are supported by GTK - they add external dependencies to GTK which are often not available by default. This kind of ruins the simplicity of a Go program consisting of a single executable file.
go-gtk is simple to use if you are used to GTK, but does not feel native when programming in Go, in my opinion.
Rendering based on OpenGL (et al.)
Fyne is simple to use and feels very native when used with Go. At first sight, I thought that this is the framework of choice. However, there is a huge drawback.
The performance of Fyne is really, really bad, at least on macOS. It consumes way too much CPU for what it does, and even though rendering would have the potential to be lightning fast, it is actually slower than native window rendering on macOS (and Windows 10). Obviously, Fyne was not designed for high performance, which is sad, because it wastes so much potential (which is there, due to the use of OpenGL). Some script based web UI frameworks actually perform a lot better than Fyne.
Example: Resizing of the Fyne example application.
Due to its performance issues, and the lack of priority on this subject in general, I really do not see any point in preferring Fyne over a web-based UI framework. However, if performance is not an issue and you are not willing to use your webbrowser, you might give it a Go.
Being a huge fan of dear imgui, I had to try its Go bindings, imgui-go. But this really feels like writing inline C within Go code. It works, most calls are available, but it does not feel native at all.
The generated imgui C bindings cimgui can also be used with Go. But that’s just too low level. If you are considering to do this, I’d suggest to just use an external C++ module to design the UI (see below).
giu basically is a layer on top of imgui-go to simplify its use and provide “native” feeling in Go.
This actually works like a charm, has great performance and is simple and straightforward to use. It is very tempting to use giu, but make sure to consider some drawbacks:
You are commiting your application to two additional layers on top of dear imgui, without corporate backing. At this time, both libraries are maintained fine, but this may change in the future.
Also, popular extensions like implot are not available with imgui-go and therefore giu.
Gio is an immediate mode UI framwork like dear imgui, but mostly written in Go. When I first saw Gio I skipped it, mainly because of the webassembly examples (I do not want a web-based UI), but as it turned out it actually is a great piece of software. Performance is a major focus (like avoiding dynamic heap allocations) and once you get used to it a little bit, it’s fun to use and lightning fast!
Still, Gio is not ideal. It is mostly maintained by only two persons, Elias Naur and Chris Waldon, and the API is not stable. There are many breaking changes, and if you are required to upgrade Gio at some point, you are probably going to have a rough time.
Alternative: Use a C++ component
Since none of the above frameworks and bindings can be recommended without reservation, an additional option to consider is to use a C++ component to implement the UI. This way, dear imgui and implot could be directly used, providing high performance and low maintenance work. For interoperability with Go, a simple high level C interface would need to be implemented.