DEV Community

Cover image for Generate Legacy .xls Files in Go Without LibreOffice — Introducing RetroXL
Mehedi Hasan Sajib
Mehedi Hasan Sajib

Posted on

Generate Legacy .xls Files in Go Without LibreOffice — Introducing RetroXL

Generate Legacy .xls Files in Go Without LibreOffice — Introducing RetroXL

Many banks, government portals, and older enterprise systems still require uploads in the legacy .xls Excel format. Go makes it easy to generate modern .xlsx files, but generating real .xls content usually requires installing LibreOffice, Python scripts, COM automation, or external system binaries.

This approach is slow, hard to deploy, and unsuitable for containers or microservice workflows.

To solve this problem, I built RetroXL, a pure-Go library that generates legacy-compatible .xls files from .xlsx, .csv, .tsv, or in-memory data structures.

Repository: https://github.com/mhshajib/retroxl


The Real Problem

If you work with banking integrations, you may have faced this issue:

  • Some bank portal rejects .xlsx
  • Some bank only accepts .xls
  • The validation rules are strict
  • The older systems cannot parse modern formats

Most Go libraries output .xlsx. Meanwhile, generating .xls typically requires tools that are heavy, slow, and not ideal for production deployments.

RetroXL addresses this directly.


What RetroXL Does

RetroXL generates .xls files using the SpreadsheetML 2003 (XML) format accepted by Excel as a valid .xls.

Key characteristics:

  • Pure Go implementation
  • Zero external dependencies or binaries
  • Converts .xlsx to .xls
  • Converts .csv and .tsv to .xls
  • Builds .xls from in-memory slices or tables
  • Outputs to file, bytes, or any io.Writer
  • Works over HTTP, gRPC, S3, etc.

Installation

go get github.com/mhshajib/retroxl
Enter fullscreen mode Exit fullscreen mode

Example 1: Convert .xlsx to .xls

package main

import (
    "log"
    "github.com/mhshajib/retroxl"
)

func main() {
    err := retroxl.ConvertXLSXToXLSFile("input.xlsx", "output.xls")
    if err != nil {
        log.Fatalf("convert failed: %v", err)
    }
}
Enter fullscreen mode Exit fullscreen mode

Example 2: Generate .xls from a slice of structs

package main

import (
    "github.com/mhshajib/retroxl"
)

type Payment struct {
    AccountNo string
    Amount    float64
    Reference string
}

func main() {
    items := []Payment{
        {"1234567890", 1200.50, "Invoice-001"},
        {"9876543210", 300.00,  "Invoice-002"},
    }

    headers := []string{"AccountNo", "Amount", "Reference"}

    var rows [][]any
    for _, p := range items {
        rows = append(rows, []any{p.AccountNo, p.Amount, p.Reference})
    }

    sheet := retroxl.FromRows("BankUpload", headers, rows)
    _ = retroxl.WriteXLSFile("bank_upload.xls", []retroxl.Sheet{sheet})
}
Enter fullscreen mode Exit fullscreen mode

Example 2: Generate .xls from a slice without structs

package main

import (
    "github.com/mhshajib/retroxl"
)

func main() {
    headers := []string{"AccountNo", "Amount", "Reference"}
    rows := [][]any{
        {"1234567890", 1200.50, "Invoice-001"},
        {"9876543210", 300.00,  "Invoice-002"},
    }

    sheet := retroxl.FromRows("BankUpload", headers, rows)
    _ = retroxl.WriteXLSFile("bank_upload.xls", []retroxl.Sheet{sheet})
}
Enter fullscreen mode Exit fullscreen mode

Example 3: Stream .xls over HTTP

func handler(w http.ResponseWriter, r *http.Request) {
    headers := []string{"AccountNo", "Amount"}
    rows := [][]any{{"123", 100}, {"456", 200}}

    sheet := retroxl.FromRows("Demo", headers, rows)

    w.Header().Set("Content-Type", "application/vnd.ms-excel")
    w.Header().Set("Content-Disposition", `attachment; filename="demo.xls"`)

    _ = retroxl.WriteXLSWriter(w, []retroxl.Sheet{sheet})
}
Enter fullscreen mode Exit fullscreen mode

When To Use RetroXL

Use RetroXL when:

  • A bank or government portal only accepts .xls
  • Your Go app generates .xlsx or .csv
  • You want a container-friendly, dependency-free solution
  • You need direct streaming over HTTP, gRPC, or cloud storage

Avoid RetroXL if you need:

  • Complex styling
  • Formulas or pivot tables
  • BIFF8 binary (.xls) output

Conclusion

RetroXL provides a clean, dependency-free way to generate legacy .xls files directly from Go. If your workflows involve banking portals, finance systems, or older enterprise applications, RetroXL removes the complexity of external conversion tools.

Repository:

https://github.com/mhshajib/retroxl

Feedback and contributions are welcome.

Top comments (0)