edn (extensible data notation, read "eed-n") is the subset of Clojure data syntax described in the edn specification, a document that is independent of Clojure. Clojure's reader reads a superset of it. edn is used for configuration files (deps.edn, bb.edn, shadow-cljs.edn), as a data transfer format (Datomic uses it), and to print Clojure data. It conveys values: it has no schema, no reference types and no object identity. There is no enclosing element at the top level, so a file or stream is a sequence of elements, which suits streaming.
General rules
- Elements, streams and files should be encoded in UTF-8.
- Elements are separated by white space. White space is not otherwise significant. Commas are white space (outside strings). The delimiters
{ } ( ) [ ]need not be separated from the elements next to them. - Tokens that begin with
#are reserved; the next character picks the meaning. Defined are#{(a set),#_(discard) and#followed by a letter (a tag).#is not a delimiter. - A
;outside a string begins a comment that runs to the next line feed.
Elements
| Element | Written | Rules |
|---|---|---|
| nil | nil | nil, null or nothing |
| Boolean | true false | on a platform with canonical values, every true is the same value |
| String | "text" | may span lines; escapes \t \r \n \\ \" |
| Character | \c \newline \return \space \tab \uNNNN | a backslash cannot be followed by white space |
| Symbol | my-ns/foo a.b | identifiers, to be read as something other than strings. They begin with a non-numeric character and contain alphanumerics and . * + ! - _ ? $ % & = < >; : and # are allowed after the first character. If -, + or . comes first, the second character, if any, must not be a digit. / may appear once in the middle, separating a prefix (often a namespace) from a name; / alone is a symbol; neither part may be empty |
| Keyword | :fred :my/fred | a symbol with a leading :; :/ and :/anything are not legal, and a keyword cannot begin with ::. All instances of one keyword are the same object where the platform interns |
| Integer | 0 -42 +7 42N | digits, an optional sign; no integer other than 0 begins with 0; 64-bit precision expected; the suffix N asks for arbitrary precision; -0 is 0 |
| Floating point | 1.5 1e10 1.5e-3 1.5M | an integer part followed by a fraction (. and digits), an exponent (e or E, an optional sign, digits), or both; or an integer followed by M; 64-bit precision expected; the suffix M asks for exact precision |
| List | (a b 42) | a sequence; may be heterogeneous |
| Vector | [a b 42] | a sequence with random access |
| Map | {:a 1, "foo" :bar, [1 2 3] four} | key and value pairs; keys and values can be of any type; each key at most once; the order of pairs means nothing |
| Set | #{a b [1 2 3]} | unique values; the order means nothing |
Tagged elements
# followed immediately by a symbol that starts with a letter is a tag: it says how to interpret the element that follows. A reader lets clients register a handler for a tag, reads the next element (which may itself be tagged), hands it to the handler, and the result is the value of tag and element together. It is not read again as edn. A tag without a handler may be reported as an error, passed to a default handler, or kept as a generic tag-and-element value.
- Tags without a prefix are reserved for built-in elements. User tags must have a prefix that the user owns or that is known to be unique:
#myapp/Person {:first "Fred" :last "Mertz"}. - A tag may accept more than one form for the element (a string and a vector, say). A tag is not an element: a tag with no element after it is an error.
#inst "1985-04-12T23:20:50.52Z"is an instant in time; the element is an RFC 3339 string.#uuid "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"is a UUID in canonical text form.
Discard and equality
#_ reads the next element, whether or not white space separates them, and throws it away: [a b #_foo 42] is [a b 42]. The discarded element must still be readable, a reader does not call tag handlers while reading it, and a discard with nothing after it is an error.
Equality, needed to keep set elements and map keys unique: nil, booleans, strings, characters and symbols are equal to values of the same type and representation. Numbers are equal only to numbers of the same magnitude, type and precision, so mixing types in keys is not advised. Lists and vectors are equal when they have the same count and equal elements in order; sets when they have the same count and every element of one has an equal in the other; maps when every entry of one has an equal key mapped to an equal value in the other. A tag defines its own equality: #uuids are equal if their canonical text is, #insts if they designate the same moment.
Reading and writing in Clojure
clojure.edn/read(([] [stream] [opts stream])) andclojure.edn/read-string(([s] [opts s])) read edn only, not Clojure code, since Clojure 1.5. The options map takes:eof(the value at end of input; otherwise end of input throws),:readers(a map of tag symbols to reader functions, tried beforedefault-data-readers) and:default(a function of the tag and the value, called when no reader is found).read-stringreturnsnilfornilor an empty string.pr,prnandpr-strprint values in a form that reads back, which is edn for plain data;print-methodcan be extended to print other types as tagged elements. Unlike edn, the Clojure readerreadalso reads code,#=and reader conditionals, and should not be used on untrusted text.
{:paths ["src" "resources"]
:deps {org.clojure/clojure {:mvn/version "1.12.0"}
io.github.user/lib {:git/tag "v1.0" :git/sha "abc1234"}}
:aliases {:dev {:extra-paths ["dev"]}}
:created #inst "2026-11-13T00:00:00Z"
:id #uuid "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"}
deps.edn, with the two built-in tags. In edn files the viewer colours symbols as names, as it does the library names here.Files
A file with the extension .edn holds one or more edn elements. The files that tools read by name are deps.edn, bb.edn, shadow-cljs.edn; see Tooling. Clojure's own data_readers.clj is Clojure code that holds one map.
Not described
Known, not yet covered
- Viewer: the viewer reads
.ednwith the Clojure reader's data forms, which are a superset of edn: ratios, radix and hexadecimal numbers,##Inf,::keywords,#"regex",#(and the quote marks are coloured as in Clojure although they are not edn, and the stricter edn rules for numbers (1.is not edn) and symbols are not checked. - Reference: libraries for edn in other languages, and the edn specification's own future BNF (the spec says it is "casual" for now), are not covered.
Unknown
- Reference: that
proutput reads back as edn for plain data, thatprint-methodprints tagged elements, and that Clojure'sreadshould not be used on untrusted text, are from knowledge of the language and not from a downloaded page (the edn specification and the docstrings ofclojure.ednwere read).
See also
- Lexical structure and the reader – the full syntax edn is a subset of
- JSON – the best-known data notation, and YAML