UCloud logo UCloud logo UCloud
v2026.3.0
  1. UCloud/Core
  2. 1. Introduction
  3. 2. Projects
  4. 3. Accounting
  5. 4. Orchestration
  6. UCloud/IM for Slurm-based HPC
  7. 5. Installation
  8. 6. Architecture and Networking
  9. 7. User and Project Management
  10. 8. Filesystem Integration
    1. 8.1. Inter-provider file transfers
  11. 9. Slurm Integration
    1. 9.1. Application Management
    2. 9.2. Built-in Applications
  12. 10. Reference
    1. 10.1. Configuration
    2. 10.2. CLI
  13. 11. Appendix
    1. 11.1. Built-in Application Index
  14. UCloud/IM for Kubernetes
  15. 12. Installation
  16. 13. Architecture and Networking
  17. 14. Filesystem Integration
  18. 15. Compute Jobs
    1. 15.1. Public Links
    2. 15.2. Public IPs
    3. 15.3. License Servers
    4. 15.4. SSH Servers
    5. 15.5. Job Audit Log
    6. 15.6. Virtual machines
  19. 16. Integrated applications
    1. 16.1. Syncthing
    2. 16.2. Integrated terminal
  20. 17. UCX applications
    1. 17.1. Hello world
    2. 17.2. Data binding
    3. 17.3. UI events
    4. 17.4. Component reference
    5. 17.5. API reference
  21. 18. Reference
    1. 18.1. Configuration
    2. 18.2. CLI
  22. Frontend development
  23. 19. Frontend description and development guidelines
  24. Branding for UCloud
  25. 20. Branding and identity for UCloud
  26. H: Procedures
  27. 21. H: Procedures
  28. 22. H: Introduction
  29. 23. H: Auditing
  30. 24. H: Auditing scenario
  31. 25. H: GitHub actions
  32. 26. H: Deployment
  33. 27. H: 3rd party dependencies (risk assesment)
  1. Links
  2. Source Code
  3. Releases

Hello world

This is a minimal UCX application with one bound input and one button event.

package main

import (
    "fmt"
    "strings"
    "sync"

    "ucloud.dk/shared/pkg/ucx"
)

type helloApp struct {
    mu      sync.Mutex   `ucx:"-"`
    session *ucx.Session `ucx:"-"`

    Name    string
    Message string
}

func NewHelloApp() ucx.Application {
    return &helloApp{
        Name:    "UCloud",
        Message: "Click the button",
    }
}

func (app *helloApp) Mutex() *sync.Mutex      { return &app.mu }
func (app *helloApp) Session() **ucx.Session  { return &app.session }
func (app *helloApp) OnInit()                 {}

func (app *helloApp) UserInterface() ucx.UiNode {
    return ucx.Flex(ucx.FlexProps{Direction: "column", Gap: 8}).
        Sx(ucx.SxP(4)).
        Children(
            ucx.H2("UCX hello world"),
            ucx.InputText("name", "Name", "Type your name", "name"),
            ucx.Button("sayHello", "Say hello", ucx.ColorPrimaryMain).
                On(ucx.UiEventClick, func(ev ucx.UiEvent) {
                    app.Message = fmt.Sprintf("Hello %s!", strings.TrimSpace(app.Name))
                }),
            ucx.TextBound("message"),
        )
}

func (app *helloApp) OnMessage(frame ucx.Frame) {
    switch frame.Opcode {
    case ucx.OpModelInput:
        app.Name = strings.TrimSpace(app.Name)
        if app.Name == "" {
            app.Message = "Please enter a name"
        }
    }
}

func main() {
    ucx.AppServe(func() ucx.Application {
        return NewHelloApp()
    })
}

What is happening

  • Name and Message are exported fields, so they are serialized into the UCX model.
  • InputText(..., "name") binds to the Name field (Name -> name by default).
  • button clicks are handled with .On(ucx.UiEventClick, ...).
  • TextBound("message") renders the Message field.
  • ucx.AppServe(...) handles handshake, mount, model patching, and event dispatch.

First extensions

From here, most real apps add:

  • validation (errors.<field> paths),
  • collections ([]struct) displayed through ucx.List(...),
  • and backend calls via ucxsvc / ucxapi when submit is clicked.
Previous UCX applications
Next Data binding