Write normal Zig or Lua functions. zigxll generates all the Excel boilerplate at compile time: exports, type conversions, registration, COM vtables. You can even develop on a Mac or Linux.
XLL add-ins are native DLLs running inside the Excel process. The C SDK is from the early 1990s. A hostile beast, zigxll makes it approachable.
Zig consumes C headers directly. No bindings or code generation step. Minimal overhead at runtime.
All the boilerplate (exports, type conversions, registration, COM vtables) is generated at compile time from your function definitions. This means high performance at runtime.
A single .xll file, a few hundred KB.
Build Windows .xll files from macOS or Linux. No Windows SDK, no Visual Studio. Tests run natively on any platform.
Excel can parallelise function calls across cores during recalculation. ZigXLL functions are thread-safe by default (MTR).
Arena allocators and explicit lifetime management. No GC pauses, no hidden allocations, predictable performance.
Write a normal Zig or Lua function. Declare it with ExcelFunction(). The framework handles everything else: type conversions, UTF-16, error mapping, Excel registration.
#VALUE! in Excel.is_async = trueconst xll = @import("xll"); pub const add = xll.ExcelFunction(.{ .name = "add", .description = "Add two numbers", .category = "My Functions", .params = &[_]xll.ParamMeta{ .{ .name = "a", .description = "First number" }, .{ .name = "b", .description = "Second number" }, }, .func = addImpl, }); fn addImpl(a: f64, b: f64) !f64 { return a + b; }
ZigXLL includes a pure Zig RTD server framework for building your own real-time data sources. Implement a handler; the framework takes care of COM, vtables, registration, and lifecycle.
One implementation built on this is the NATS connector, which turns Excel into a reactive computation engine. Subscribe to message streams with a formula. Derive cross-rates, calculate rolling statistics, publish alerts back. All in the spreadsheet.
The NATS.SUBWIN() function provides windowed buffers for time-series analysis directly in cells.
Not everything needs to be Zig. For many operations, Lua is as fast as Zig! Annotate standard Lua functions and the framework generates Excel UDFs from them automatically. Scripts are embedded in the .xll at build time.
The Lua runtime is sandboxed (no filesystem, no shell access) and thread-safe by default. A pool of independent Lua states allows parallel recalculation with no contention.
@param, @async, @categoryxll.get/xll.set--- Calculate hypotenuse -- @param a number Side a -- @param b number Side b function hypotenuse(a, b) return math.sqrt(a * a + b * b) end --- Fibonacci with async support -- @param n number Index -- @async function slow_fib(n) local a, b = 0, 1 for i = 1, n do a, b = b, a + b end return a end
Add .is_async = true to any function definition. This make it run on a background thread pool. The cell shows #N/A while computing, then updates with the result automatically.
Results are cached, keyed by function arguments, so subsequent recalculations return instantly. Works for both Zig and Lua functions. Useful for slow computations, network calls, or anything you don't want blocking Excel's main thread.
pub const slow_calc = xll.ExcelFunction(.{ .name = "SlowCalc", .description = "Heavy computation", .is_async = true, .params = &[_]xll.ParamMeta{ .{ .name = "n" }, }, .func = slowCalcImpl, }); fn slowCalcImpl(n: f64) !f64 { // Runs on a background thread. // Cell shows #N/A, then updates // when complete. return expensive_work(n); }
Use the GitHub template to scaffold a new repo with everything wired up, or add zigxll as a dependency in an existing project's build.zig.zon:
.xll = .{ .url = "https://github.com/alexjreid/zigxll/archive/refs/tags/v0.3.1.tar.gz", .hash = "..." }
Write Zig functions, declare them with ExcelFunction(). List your modules in main.zig:
pub const function_modules = .{ @import("my_functions.zig") };
Cross-compiles to a Windows .xll from any platform. Tests run natively.
zig build # produces zig-out/lib/my_functions.xll
zig build test # runs tests natively, no Windows SDK needed
Double-click zig-out/lib/my_functions.xll to load in Excel.
The motivation behind zigxll. Why Zig, how comptime eliminates boilerplate, and real-world implementation details.
Using zigxll's NATS connector to turn Excel into a reactive stream processor for FX monitoring.
Full docs covering function definitions, real-time data servers, embedded Lua scripting, and how it all works.
A full example project you can clone, build, and load into Excel.
Start a new project with the template. Everything set up and ready to go.
Stream NATS messages into Excel as live data. Subscribe, derive, publish back. ~100KB binary.
Clone the template, write your functions, run zig build, load in Excel.