UNFINISHED: Sharp corners to avoid when working on TUIs with Bubble Tea

Originally posted on 2023-12-20

Disclaimer: Bubble Tea from charm_ is incredible and I love it. This page is only meant to help people avoid the mistakes I made.

TODO: Add code samples / screenshots

Updating the model outside of the update function

You need to update your model in the update function and return the new model so it can be rendered. You can get away with updating values via pointers but it isn’t recommended.

Passing a reference to your update function so other code can call it

This is tempting with event handlers but don’t do it! Why? Because your update function will return a new model and your new model will be thrown away.

If you need to update your model outside of the main loop (e.g. to indicate progress on a long running process that was already kicked off by a tea.Cmd) then get a reference to your program and use Program.Send(msg). In most cases this will be done by making your program a global variable like this:

var program *tea.Program


func main() {
	p = tea.NewProgram(initialModel())

	if _, err := p.Run(); err != nil {

We all hate global variables so if there’s a better way please let me know.

Forgetting to append commands

Don’t forget to append commands and return them with tea.Batch

Exiting your update function early and not letting everything update

Don’t just return your next command. Other components might depend on the update you just consumed (especially Tick).

Forgetting to initialize components

I’ve had this with progress bars quite a few times

Using the wrong update function

Progress bars only work for me with the ViewAs function. I’ve never got them to work with a normal call to Update.

Updating state in the view

I was unable to properly manage the position of my viewport since I updated the state of my viewport in the view. Don’t do this!