Irgo
Native app development framework for mobile and desktop using Go and Datastar.
Install / Use
/learn @stukennedy/IrgoREADME
Irgo
A hypermedia-driven application framework that uses Go as a runtime kernel with Datastar. Build native iOS, Android, and desktop apps using Go, HTML, and Datastar - no JavaScript frameworks required.
Key Features
- Go-Powered Apps: Write your backend logic in Go, compile to native mobile frameworks or desktop apps
- Datastar for Interactivity: Use Datastar's hypermedia approach with SSE instead of complex JavaScript
- Cross-Platform: Single codebase for iOS, Android, desktop (macOS, Windows, Linux), and web
- Virtual HTTP (Mobile): No network sockets - requests are intercepted and handled directly by Go
- Native Webview (Desktop): Real HTTP server with native webview window
- Type-Safe Templates: Use templ for compile-time checked HTML templates
- Hot Reload Development: Edit Go/templ code and see changes instantly
Architecture
Mobile Architecture (iOS/Android)
┌─────────────────────────────────────────────────────────────┐
│ Mobile App │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ WebView (Datastar) │ │
│ │ • HTML rendered by Go templates │ │
│ │ • Datastar handles interactions via irgo:// scheme │ │
│ └──────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼────────────────────────────────┐ │
│ │ Native Bridge (Swift / Kotlin) │ │
│ │ • Intercepts irgo:// requests │ │
│ │ • Routes to Go via gomobile │ │
│ └──────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼────────────────────────────────┐ │
│ │ Go Runtime (gomobile bind) │ │
│ │ • HTTP router (chi-based) │ │
│ │ • Template rendering (templ) │ │
│ │ • Business logic │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Desktop Architecture (macOS/Windows/Linux)
┌─────────────────────────────────────────────────────────────┐
│ Desktop App │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Native Webview Window │ │
│ │ (System webview engine - Chromium/WebKit) │ │
│ │ Navigates to: http://localhost:PORT │ │
│ └──────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼────────────────────────────────┐ │
│ │ Go HTTP Server (localhost:PORT) │ │
│ │ • Page Routes (Templ → HTML) │ │
│ │ • API Routes (SSE responses) │ │
│ │ • Static Asset Server (/static/*) │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Quick Start
Prerequisites
- Go 1.21+
- templ:
go install github.com/a-h/templ/cmd/templ@latest - air:
go install github.com/air-verse/air@latest
For mobile development:
- gomobile:
go install golang.org/x/mobile/cmd/gomobile@latest && gomobile init - entr:
brew install entr(macOS) - For iOS: Xcode with iOS Simulator
- For Android: Android Studio with SDK and emulator
For desktop development:
- CGO enabled (C compiler required)
- macOS: Xcode Command Line Tools (included with Xcode)
- Windows: MinGW-w64 or similar C compiler
- Linux: GCC and WebKit2GTK dev packages (
apt install libwebkit2gtk-4.0-dev)
Install Irgo CLI
go install github.com/stukennedy/irgo/cmd/irgo@latest
Or build from source:
git clone https://github.com/stukennedy/irgo.git
cd irgo/cmd/irgo
go install .
Create a New Project
irgo new myapp
cd myapp
go mod tidy
bun install # or: npm install
Run as Desktop App
irgo run desktop # Run as desktop app
irgo run desktop --dev # With devtools enabled
Development with Hot Reload (Web)
irgo dev # Start dev server at http://localhost:8080
iOS Development
irgo run ios --dev # Hot-reload with iOS Simulator
irgo run ios # Production build
Build for Production
# Desktop
irgo build desktop # Build for current platform
irgo build desktop macos # Build macOS .app bundle
irgo build desktop windows # Build Windows .exe
irgo build desktop linux # Build Linux binary
# Mobile
irgo build ios # Build iOS framework
irgo build android # Build Android AAR
Project Structure
myapp/
├── main.go # Mobile/web entry point (build tag: !desktop)
├── main_desktop.go # Desktop entry point (build tag: desktop)
├── go.mod # Go module definition
├── .air.toml # Air hot reload configuration
├── package.json # Node dependencies (Tailwind CSS)
│
├── app/
│ └── app.go # Router setup and app configuration
│
├── handlers/
│ └── handlers.go # HTTP handlers (business logic)
│
├── templates/
│ ├── layout.templ # Base HTML layout
│ ├── pages.templ # Page templates
│ └── components.templ # Reusable components
│
├── static/
│ ├── css/
│ │ ├── input.css # Tailwind source
│ │ └── output.css # Generated CSS
│ └── js/
│ └── datastar.js # Datastar library
│
├── mobile/
│ └── mobile.go # Mobile bridge setup
│
├── ios/ # iOS Xcode project
├── android/ # Android project
│
└── build/
├── ios/ # Built iOS framework
├── android/ # Built Android AAR
└── desktop/ # Built desktop apps
├── macos/ # macOS .app bundle
├── windows/ # Windows .exe
└── linux/ # Linux binary
Desktop Development
How Desktop Mode Works
Desktop mode uses a different architecture than mobile:
- Real HTTP Server: A Go HTTP server starts on an auto-selected localhost port
- Native Webview: A native window with an embedded browser engine opens
- Standard HTTP: The webview navigates to the localhost URL - standard HTTP requests
This means your app works identically to the web dev server, but packaged as a native desktop app.
Desktop Entry Point
Projects include a main_desktop.go with build tag //go:build desktop:
//go:build desktop
package main
import (
"flag"
"fmt"
"net/http"
"myapp/app"
"github.com/stukennedy/irgo/desktop"
)
func main() {
devMode := flag.Bool("dev", false, "Enable devtools")
flag.Parse()
r := app.NewRouter()
mux := http.NewServeMux()
staticDir := desktop.FindStaticDir()
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
mux.Handle("/", r.Handler())
config := desktop.DefaultConfig()
config.Title = "My App"
config.Debug = *devMode
desktopApp := desktop.New(mux, config)
fmt.Println("Starting desktop app...")
if err := desktopApp.Run(); err != nil {
fmt.Printf("Error: %v\n", err)
}
}
Desktop Configuration
config := desktop.Config{
Title: "My App", // Window title
Width: 1024, // Window width
Height: 768, // Window height
Resizable: true, // Allow window resize
Debug: false, // Enable browser devtools
Port: 0, // 0 = auto-select available port
}
Running Desktop Apps
# Run directly (compiles and runs)
irgo run desktop
# With devtools (for debugging)
irgo run desktop --dev
Building Desktop Apps
# Build for current platform
irgo build desktop
# Build for specific platform
irgo build desktop macos # Creates build/desktop/macos/MyApp.app
irgo build desktop windows # Creates build/desktop/windows/MyApp.exe
irgo build desktop linux # Creates build/desktop/linux/MyApp
Desktop vs Mobile: Key Differences
| Aspect | Mobile | Desktop |
|--------|--------|---------|
| HTTP | Virtual (no sockets) | Real localhost server |
| Bridge | gomobile + native code | None (direct HTTP) |
| Entry point | main.go | main_desktop.go |
| Build tag | !desktop | desktop |
| CGO | Not required | Required (webview) |
Writing Handlers
Irgo supports two types of handlers:
Standard Handlers (Full Page Loads)
Return (string, error) with HTML:
r.GET("/about", func(ctx *router.Context) (string, error) {
return renderer.Render(templates.AboutPage())
})
Datastar SSE Handlers
Return error and use ctx.SSE() for responses:
r.DSPost("/todos", func(ctx *router.Context) error {
var signals struct {
Title string `json:"title"`
}
ctx.ReadSignals(&signals)
todo := createTodo(signals.Title)
sse := ctx.SSE()
sse.PatchTempl(templates.TodoItem(todo))
sse.PatchSignals(map[string]any{"title": ""}) // Clear input
return nil
})
Writing Templates
Templates use templ with Datastar attributes:
// templates/pages.templ
package templates
templ HomePage() {
@Layout("Home") {
<main class="container mx-auto p-4">
<h1 class="text-2xl font-bold">Welcome to Irgo</h1>
<div data-signals="{name: ''}">
<input
