A guided explainer
Reach for what you already ship
The Google Closure Library is already inside every ClojureScript build. Here is what that means — and how to know when to use it instead of an npm package, the standard library, or the browser.
You are three days into a ClojureScript project. A search box is firing a
request on every keystroke, and you want it to wait until the typing
stops — to debounce. Your fingers already know the answer,
npm install lodash.debounce, and they are halfway through
typing it.
Wait. You almost certainly already have debounce. It is in
your build right now — written, tested, shipped by Google —
and you didn’t add it. This page is about the large, useful library
that arrives with ClojureScript whether you notice it or not, why it is
so easy to miss, and the small guide we built so that you (and the AI
agent writing alongside you) stop reinstalling things you already own.
What is already in the box
One fact about how ClojureScript is built explains the whole thing.
ClojureScript does not compile to JavaScript on its own; it compiles
through Google’s Closure Compiler, the
optimizer that shrinks and checks the final code. And bundled with that
compiler is its companion, the Google Closure
Library
Everything in it lives under the goog. name —
goog.functions, goog.crypt,
goog.i18n, and hundreds more. That is why you’ll see
“goog” throughout this page; it just means
“from the Closure Library.”
— a batteries-included JavaScript toolkit Google has
maintained and shipped in production (Gmail, Maps, Docs) for well over a
decade.
It is not an add-on. cljs.core — the standard library
you use in every file — is itself built on top of it. Every
ClojureScript build already contains it, and any namespace can reach it
with a plain require.
The version is pinned to the exact one the ClojureScript compiler uses,
so what you ship and what the docs describe are always the same library
— no drift, no separate install to keep in sync.
The Closure Library is not something you install. It is already
in the box you are holding.
So why does nobody reach for it?
Two reasons, and neither is that the library is missing things. The first is size: it is enormous. Read straight from the compiler, it holds 68,083 named pieces — functions, classes, constants. The second is that its documentation is written for JavaScript developers and organized strictly by name, with no view of what matters to someone writing a modern ClojureScript app.
Faced with a wall that size, the path of least resistance is the npm package whose name you already know. So the library quietly has the function you need — debounce, a hash, a color conversion, a 64-bit integer — and you install a dependency for it anyway. The problem was never absence. It was discoverability, and judgment: knowing the thing exists, and knowing when it is the right call.
First we made it findable
The obvious fix is a good map, so that is what we built first: a tool that reads the entire library straight out of the compiler and writes it back out as documentation tuned for ClojureScript — every symbol with the exact call form you’d type.
Under the hood: how the map gets made
Three steps, fully automatic. Extract: ask the Closure Compiler itself for every symbol it knows, so nothing is guessed — the source of truth is the compiler, not a human. Enrich: work out, for each symbol, the precise ClojureScript form to call it and which symbols inherit from which. Render: write it all out as plain pages. The result is about 2,196 pages, one per symbol, plus a catalog that lists them all.
It is faithful and it is complete. But complete is not the same as actionable. A machine can only organize the catalog the one way it can derive automatically: by name — 314 sections, one per namespace, alphabetical. Which is useless at the exact moment you need it, because you don’t know the name yet. You know the capability: “debounce.” Watch what each version gives back when that is all you have:
Search the catalog for “debounce” and you land on a heading:
## goog.functions
No description — a list of function names has no prose to show. Nothing tells you this namespace is where debounce lives, or that you want it over the heavyweight class one section away. A list of every street is not directions.
The same search against the curated index lands on one line:
debounce → goog.functions.debounce · replaces lodash.debounce
The need, the exact symbol to call, and the npm package you can now skip — in a line you can read at a glance.
The piece a machine can’t write
A reference answers “what is
goog.crypt.Sha256?” That is a fine question, but it is
not the one you have. Yours is: “I need to hash a string —
what do I reach for, and should I even use the Closure Library
here?”
That second question is judgment, and no amount of reading the
library produces it automatically. Sometimes the Closure Library is the
right answer. Sometimes the standard library already does it better and
reaching for goog. would be a step backward. Sometimes the
browser itself has grown the feature since the library was written.
Telling those apart is a human call — so we made it, by hand, one
capability at a time, and wrote it down where the tools (and the agents)
can read it.
Four answers, not one
Each entry in the guide starts with a need, names the npm package you might otherwise install, points at the Closure symbol — and gives one of four verdicts. The verdict is the judgment; it is the part that matters.
CL Use the Closure
Library. Core doesn’t have this, and the library does it
well. Debouncing is the clean example: reach for
goog.functions.debounce and skip lodash.debounce
entirely.
core Use the standard
library instead. Here cljs.core already wins, and
reaching for goog. would be the wrong move. Need a UUID?
It’s cljs.core/random-uuid — not an npm package,
and not the Closure Library, which has no real UUID at all.
native-first The
browser already has it. Try the built-in first; keep the
library for the harder edges. Formatting a date for a locale is
Intl.DateTimeFormat in any modern browser — lean on
the Closure Library only when you need its pattern language.
gap Nobody has it
cleanly — here’s the honest workaround. An automatic
retry-with-backoff, say: the library gives you the backoff schedule
(goog.math.ExponentialBackoff), but you still drive the retry
loop yourself. The guide says so plainly rather than overselling.
That is the whole idea, repeated across every common need. The full set is the Field Guide — a table you can search and filter by verdict.
Why you can trust it
A hand-written guide has a failure mode a generated one doesn’t: it can quietly go wrong. A symbol gets renamed in a library update and the guide keeps pointing at a name that no longer exists — a confident, dead reference, which is worse than no guide at all.
So the guide doesn’t rely on us having been careful. Every Closure symbol it names is checked against the real library on every single build, and if even one has gone missing, the build stops and says which. The curation is human; the proof that it’s still true is automatic. How we keep it honest walks through exactly how, and why that matters whenever you build a guide of your own.
How to use it
The curated index rides at the top of the file an AI agent reads first when it works in your codebase, so the agent gets the same four-way judgment you do — reach for the library, or core, or the browser, or roll it yourself. The Field Guide is the same thing for human eyes.
But you don’t need either open to get the benefit. The habit is the
point. Next time your fingers start typing npm install for
something small and standard — a hash, a debounce, a color tweak, a
date format — pause for one second and ask the question this whole
project is built around:
Is this already in the box?
Grounded in the goog-docs project — figures (68,083
symbols, ~2,196 pages, 314 catalog sections) are read from the live model,
not estimated. ·
The Field Guide ·
Keeping it honest