A NATS connector for Excel built with ZigXLL.
Subscribes to NATS subjects with =NATS.SUB("subject") to stream published messages into Excel cells, via RTD. Can also publish with =NATS.PUB("subject", cell-ref). Read on for usage instructions.
Watch the full demo on YouTube | Blog post
NATS is a lightweight messaging system that acts as a convenient hub for all kinds of real-time data: IoT sensor readings, financial market prices, application metrics, logistics tracking, machine telemetry, and more. This add-in lets you tap into any of that data directly in Excel, with no code, bridging scripts, or CSV exports needed. Subscribe to live subjects, build formulas over streaming values, create charts and visualisations that update in real time, and publish computed values back to NATS from cells. If your data flows through NATS, it can flow into (and out of) Excel.
- Single small binary (~480KB
.xllfile). Just open it in Excel, or install. (If you need tls:// connections, Win64 OpenSSL v3 is required. Download .msi. This is a fairly common requirement so you may have it already.) - Built on nats.c, the official, battle-tested NATS C client. Supports TLS, NKey, token, and credentials file authentication out of the box.
- Supports PUB snd SUB, with more on the way.
- No .NET framework dependencies, no COM setup, just run it The RTD server registers itself to
HKCUon load, no admin rights needed. - Custom Excel RTD wrapper functions like
=NATS.SUB("prices.gbp")so users never need to think about raw=RTD(...)syntax. You can create your own wrapper functions to your subjects, i.e. building a subject string based on multiple, documented parameters. - File-based configuration, with
config.json- details below. - Windowed subscriptions.
=NATS.SUBWIN("subject", 100)accumulates the last N numeric values in a ring buffer, spilling them as a dynamic array for rolling aggregates, charts, and sparklines. - Type hints and JSON extraction.
=NATS.SUB("ticker.AAPL", "$.price")parses JSON payloads and returns native Excel types - numbers, bools, strings - ready for formulas and charts. - Designed for high throughput. Arena-allocated refresh cycles, zero per-message allocations on the render path, and lock-free handoff from the nats.c thread pool to Excel's RTD polling.
=NATS.SUB("my.subject")
Or the raw RTD call:
=RTD("zigxll.connectors.nats", , "my.subject")
Requires a running NATS server. By default connects to 127.0.0.1:4222 - see Configuration to change this.
Excel throttles RTD updates via Application.RTD.ThrottleInterval, which defaults to 2000ms. For higher update rates, lower it in the VBA Immediate Window (Alt+F11, then Ctrl+G):
Application.RTD.ThrottleInterval = 100Set to 0 for the fastest possible updates. This setting persists across sessions.
Connection settings are loaded from a config.json file. The search order is:
- Same directory as the
.xllfile %APPDATA%\zigxll-nats\config.json
If no file is found, defaults are used (nats://127.0.0.1:4222, no auth, no TLS). All fields are optional.
{
"url": "nats://myserver:4222",
"name": "excel-nats"
}To connect to NGS or any Synadia Cloud deployment, use the TLS variant of the XLL and a .creds file exported from your account:
{
"url": "tls://connect.ngs.global",
"name": "zigxll-nats-test",
"credentials_file": "C:\\Users\\you\\Desktop\\ngs.creds"
}This also applies to any self-hosted NATS deployment using operator/account JWTs with credentials files.
| Field | Type | Description |
|---|---|---|
url |
string | Server URL. Default nats://127.0.0.1:4222 |
servers |
string[] | Multiple server URLs for cluster failover (overrides url) |
name |
string | Connection name (visible in NATS monitoring) |
user |
string | Username for user/password auth |
password |
string | Password for user/password auth |
token |
string | Auth token |
credentials_file |
string | Path to .creds file (JWT + NKey, for NATS NGS / operator mode) |
nkey_public |
string | NKey public key (the N... string) |
nkey_seed_file |
string | Path to NKey seed file (requires nkey_public) |
tls |
bool | Enable TLS (requires OpenSSL for Windows). Default false |
tls_ca_cert |
string | Path to CA certificate file |
tls_cert |
string | Path to client certificate file |
tls_key |
string | Path to client private key file |
tls_skip_verify |
bool | Skip server cert verification (dev only). Default false |
connect_timeout_ms |
int | Connection timeout in milliseconds |
ping_interval_ms |
int | Ping interval in milliseconds |
max_reconnect |
int | Max reconnect attempts (-1 for unlimited) |
reconnect_wait_ms |
int | Wait between reconnect attempts in milliseconds |
Although Excel XLL assemblies only run on Windows, ZigXLL cross-compiles Windows XLL add-ins from macOS or Linux with the help of xwin.
Windows: Skip this section.
macOS:
brew install xwin
xwin --accept-license splat --output ~/.xwinLinux:
cargo install xwin
xwin --accept-license splat --output ~/.xwinIf Cargo isn't available, install Rust via rustup.rs or download a prebuilt binary from the xwin releases page.
See the ZigXLL README for more details.
Note: Cross-compilation does not yet support TLS (-Dtls=true), as it requires OpenSSL .lib files for the target platform to be available locally. CI builds natively on Windows with TLS enabled.
zig buildThe XLL will be output to zig-out/lib/zigxll-connectors-nats.xll.
| ProgID | CLSID | Description |
|---|---|---|
zigxll.connectors.nats |
{F3910D1B-338F-49D4-A364-B113EA6CE115} |
Subscribe to NATS subjects |
RTD servers are registered automatically when the XLL is loaded into Excel (writes to HKCU\Software\Classes, no admin needed).
| Function | Description |
|---|---|
=NATS.SUB("subject") |
Subscribe to a NATS subject (RTD wrapper) |
=NATS.SUB("subject", "type") |
Subscribe with a type hint (see below) |
=NATS.SUBWIN("subject", n) |
Subscribe and accumulate the last N numeric values |
=NATS.SUBWIN.VALS(handle) |
Read accumulated values as a spilling column |
=NATS.PUB("subject", "payload") |
Publish a message to a NATS subject |
=NATS.INFO() |
Connection info and statistics (spills a 2-column matrix) |
The optional second argument to NATS.SUB controls how the received payload is interpreted:
| Usage | Behavior |
|---|---|
=NATS.SUB("prices.BTC") |
Auto: duck-types - tries bool, int, float, falls back to string |
=NATS.SUB("prices.BTC", "number") |
Forces numeric coercion, #VALUE! if not parseable |
=NATS.SUB("flags.active", "bool") |
Forces bool ("true"/"false"), #VALUE! otherwise |
=NATS.SUB("data.raw", "string") |
Always string, no coercion |
=NATS.SUB("ticker.AAPL", "$.price") |
Parses JSON payload, extracts price field, type from JSON |
=NATS.SUB("ticker.AAPL", "$.quote.last") |
Nested dot-path: {"quote":{"last":142.5}} → 142.5 as double |
When omitted, the default is auto - values that look like numbers or booleans are returned as native Excel types, enabling direct use in formulas and charts without manual conversion.
NATS.SUBWIN subscribes to a NATS subject and accumulates the last N numeric values in a ring buffer, rather than showing only the latest value. It returns a handle string that updates on each new message. NATS.SUBWIN.VALS takes that handle and returns the buffered values as a spilling column.
A1: =NATS.SUBWIN("sensor.temperature", 100)
B1: =NATS.SUBWIN.VALS(A1)
A1 displays a handle like sensor.temperature#42 that changes with every new value. B1 spills up to 100 values downward (oldest first), automatically resizing as the buffer fills. Non-numeric messages are silently dropped.
Because NATS.SUBWIN.VALS returns a dynamic array, you can use standard Excel functions directly over the spill range:
C1: =AVERAGE(NATS.SUBWIN.VALS(A1))
C2: =MAX(NATS.SUBWIN.VALS(A1))
C3: =STDEV(NATS.SUBWIN.VALS(A1))
You can also select the spill range as a data source for sparklines or charts via the Excel UI, giving you a live-updating plot with no VBA.
This is useful for rolling statistics over sensor data, tracking price movements, visualising rate-of-change, or any situation where you need recent history rather than just the latest value. The maximum window size is 4096.
NATS.PUB publishes a message to NATS and displays PUB: <subject> on success. Because it's a regular Excel function, it re-publishes automatically whenever its input cells change - making it reactive.
=NATS.PUB("commands.set-target", B2)
When B2 changes, the new value is published to commands.set-target immediately. This makes it easy to build two-way Excel-NATS systems: subscribe to live data with NATS.SUB, transform it with formulas, and publish results back with NATS.PUB. Who'd have thought, stream processing (kind of) in Excel.
The connection is shared with NATS.SUB. If no RTD subscriptions are active, NATS.PUB establishes the connection lazily on first use.
Returns #VALUE! if not connected or publish fails.
NATS.INFO returns a spilling 2-column matrix with connection metadata and message statistics from nats.c. Useful for debugging.
The XLL embeds a vendored copy of nats.c, compiled as a static library. This required some patches to work in the Zig XLL build environment (bypassing InitOnceExecuteOnce and atexit which deadlock without a full MSVC CRT). It may be possible to use nats.c as a Zig build system dependency instead of vendoring, but this hasn't been explored yet. Messages arrive on nats.c's internal thread pool via a subscription callback, which stores the latest value per topic and notifies Excel to refresh.
Key implementation details:
- Arena allocator for UTF-16 string conversions during Excel refresh cycles -reset once per
RefreshDatabatch, zero malloc/free churn on the hot path - Null-terminated subject copies when passing Zig slices to the nats.c C API
- CRT compatibility patches for the Zig XLL build environment -
InitOnceExecuteOnceandatexit()are bypassed as they can deadlock in DLLs built with Zig's CRT stubs
Pre-built, signed XLLs for 64-bit Excel on Windows:
| Variant | Download | Notes |
|---|---|---|
| Standard | zigxll-connectors-nats-notls.xll | No external dependencies |
| With TLS | zigxll-connectors-nats.xll | Requires Win64 OpenSSL v3 (64-bit) |
- Download one of the XLLs above
- You may need to unblock it: Excel is blocking untrusted XLL add-ins
- Double-click the
.xllfile to load it into Excel
- JetStream: subscribe to JetStream consumers for durable, replay-capable streams with at-least-once delivery
- Last value population: populate cells with the most recent value on subscribe, so sheets aren't empty until the next publish
- Debouncing/windowing: reduce unnecessary recalculations, e.g. round to 3 decimal places and only update the cell if the rounded value differs
- Reconnect status in cells: surface connection state (connected/reconnecting/disconnected) so users can see when the link is down
MIT. See LICENSE for details.
This project uses nats.c (Apache 2.0) and ZigXLL (MIT). See NOTICE for attribution.


