Project Structure

An irgo project follows a clean, conventional structure that separates concerns while keeping related code together.

Directory Layout

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 4)├── app/│   └── app.go           # Router setup and app configuration├── handlers/│   └── handlers.go      # HTTP handlers (business logic)├── templates/│   ├── layout.templ     # Base HTML layout│   ├── home.templ       # Home page template│   └── components.templ # Reusable components├── static/│   ├── css/│   │   ├── input.css    # Tailwind source│   │   └── output.css   # Generated CSS│   └── js/│       ├── htmx.min.js  # HTMX library (downloaded automatically)│       └── hx-ws.js     # HTMX WebSocket extension├── 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

Entry Points

irgo uses Go build tags to separate platform-specific entry points:

Mobile/Web Entry Point

main.go
//go:build !desktoppackage mainimport (    "fmt"    "log"    "net/http"    "os"    "myapp/app"    "github.com/stukennedy/irgo/mobile")func main() {    // If run with "serve" argument, start web dev server    if len(os.Args) > 1 && os.Args[1] == "serve" {        runDevServer()        return    }    // Otherwise, initialize for mobile    initMobile()}func initMobile() {    mobile.Initialize()    r := app.NewRouter()    mobile.SetHandler(r.Handler())    fmt.Println("Mobile app initialized")}func runDevServer() {    r := app.NewRouter()    mux := http.NewServeMux()    mux.Handle("/static/", http.StripPrefix("/static/",        http.FileServer(http.Dir("static"))))    mux.Handle("/", r.Handler())    fmt.Println("Dev server at http://localhost:8080")    log.Fatal(http.ListenAndServe(":8080", mux))}

Desktop Entry Point

main_desktop.go
//go:build desktoppackage mainimport (    "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)    }}
Build Tags

The //go:build !desktop and //go:build desktop tags ensure only the correct entry point is compiled for each platform. The irgo CLI handles this automatically.

App Configuration

The app/ directory contains your router setup and application configuration:

app/app.go
package appimport (    "myapp/handlers"    "github.com/stukennedy/irgo/pkg/render"    "github.com/stukennedy/irgo/pkg/router")func NewRouter() *router.Router {    r := router.New()    renderer := render.NewTemplRenderer()    // Mount handlers    handlers.Mount(r, renderer)    return r}

Handlers

The handlers/ directory contains your HTTP handlers. Group related handlers into separate files:

handlers/handlers.go
package handlersimport (    "myapp/templates"    "github.com/stukennedy/irgo/pkg/render"    "github.com/stukennedy/irgo/pkg/router")var renderer *render.TemplRendererfunc Mount(r *router.Router, rend *render.TemplRenderer) {    renderer = rend    // Pages    r.GET("/", HomePage)    r.GET("/about", AboutPage)    // API routes    r.Route("/api", func(r *router.Router) {        r.GET("/users", ListUsers)        r.POST("/users", CreateUser)    })}

Templates

The templates/ directory contains your templ templates. Organize by feature or page:

templates/├── layout.templ       # Base HTML structure├── home.templ         # Home page├── about.templ        # About page├── components/│   ├── nav.templ      # Navigation component│   ├── footer.templ   # Footer component│   └── card.templ     # Reusable card component└── users/    ├── list.templ     # User list page    ├── form.templ     # User form    └── item.templ     # Single user item

Static Assets

The static/ directory serves static files like CSS, JavaScript, and images:

static/├── css/│   ├── input.css      # Tailwind source file│   └── output.css     # Generated CSS (git-ignored)├── js/│   ├── htmx.min.js    # HTMX library (downloaded by irgo new)│   ├── hx-ws.js       # HTMX WebSocket extension│   └── app.js         # Custom JavaScript (if needed)└── images/    └── logo.png       # Static images
Tip

The output.css is generated by Tailwind during build. Add it to.gitignore and generate it as part of your build process.

Build Output

Built artifacts are placed in the build/ directory:

build/├── ios/│   └── Irgo.xcframework/    # iOS framework for Xcode├── android/│   └── irgo.aar             # Android archive└── desktop/    ├── macos/    │   └── MyApp.app/       # macOS application bundle    ├── windows/    │   └── MyApp.exe        # Windows executable    └── linux/        └── myapp            # Linux binary

Next Steps