Getting Started

Tutorial: Server Monitor

Build your first real Tako application step-by-step.

In this tutorial, we will build a functional Server Monitor application. You will learn how to register Views, use the Router, handle global Keyboard inputs, and utilize the built-in Logger—all in one smooth flow.

1. Setting up the App

First, let's create our main.go file and initialize the Tako framework.

package main

import (
    "gettako.dev/tako"
    "gettako.dev/tako/contracts"
)

func main() {
    app := tako.NewApp()
    
    // We will register our routes here shortly

    tako.Run(app)
}
Pro Tip: You don't need to manually configure the Bubble Tea engine. tako.Run(app) automatically handles booting providers, resolving the TUI manager, and starting the event loop!

2. Creating the Dashboard View

In Tako, everything drawn on the screen is a View. A View only needs to implement a single Render(ctx) method. Let's create our Dashboard.

type DashboardScreen struct {
    serverStatus string
}

func (s *DashboardScreen) Render(ctx contracts.Context) string {
    return "=== SERVER MONITOR ===\n\n" +
        "Status: " + s.serverStatus + "\n\n" +
        "[P] Ping Server  |  [Q] Quit"
}

3. Registering the Route

Now that we have our DashboardScreen, we need to tell Tako's Router about it. Let's update our main.go file.

func main() {
    app := tako.NewApp()
    
    // Register the "dashboard" route
    app.TUI().Router().Route("dashboard", func() contracts.View {
        return &DashboardScreen{
            serverStatus: "Idle",
        }
    })

    // Tell the router to start on this screen
    app.TUI().Router().Navigate("dashboard")

    tako.Run(app)
}

If you run go run main.go right now, you will see your Dashboard! But pressing P or Q doesn't do anything yet.

4. Handling Global Inputs

Let's make our app interactive. We want Q to quit the app, and P to simulate pinging the server. We can do this using the Input().Global() manager.

func main() {
    app := tako.NewApp()
    
    // ... (routing code) ...

    // Bind Q and Ctrl+C to quit
    app.Input().Global().BindAny([]string{"q", "ctrl+c"}, func(ctx contracts.InputContext) {
        ui, _ := contracts.Resolve[contracts.UIManager](app, "ui.manager")
        ui.Quit()
    })

    // Bind P to Ping
    app.Input().Global().Bind("p", func(ctx contracts.InputContext) {
        app.Logger().Info("Ping requested by user!")
        
        // In a real app, you would dispatch an event or update state here
        app.Events().Dispatch("server.pinged", nil)
    })

    tako.Run(app)
}
Why use Global Binds? Global keybindings are active no matter what screen you are on. If you only wanted P to work on the Dashboard, you would make DashboardScreen implement the contracts.KeyBinder interface instead!

5. Adding the Logger

Did you notice we called app.Logger().Info() above?

Because TUIs take over your entire terminal screen, using fmt.Println will completely break your layout. Tako includes an asynchronous, file-based Logger out of the box.

Run your app, press P, and then press Q to exit.

Now, look in your project directory. You will see a hidden folder .tako/logs/tako.log. Open it, and you'll see your perfectly formatted log entry!

tako.log
time=2024-05-12T10:00:00.000Z level=INFO msg="Ping requested by user!"

Conclusion

Congratulations! 🎉

In just a few lines of code, you have built a TUI app with routing, keybindings, event dispatching, and file-logging.

Next, check out Architecture Concepts to understand how Tako achieves this magic under the hood.

Created by Octopy ID · Maintained under the GetTako
© 2026 Tako · MIT License