Custom formats
A format teaches Jx a new file type.
.json
is the single native built-in — Jx
is
JSON — so every other extension a project opens, saves, builds, or loads content from (
.md
,
.csv
, your
.toml
) is dispatched through a format class. A class participates in format dispatch iff its
.class.json
descriptor carries a top-level
format
object.
The
format
block
The parser's
Markdown.class.json
declares, verbatim:
"format": {
"extensions": [".md"],
"mediaType": "text/markdown",
"documentKinds": ["page", "component", "content"],
"exportTarget": true,
"remote": false
}
| Key | Type | Default | Meaning |
|---|---|---|---|
extensions |
string[]
(required) |
— | File extensions claimed, with leading dot. |
mediaType |
string |
— | MIME type; used for icons, labels, HTTP responses. |
documentKinds |
("page"|"component"|"content")[] |
[] |
page
/
component
admit the extension into pages/components discovery globs;
content
admits it as a content source. |
exportTarget |
boolean |
false |
When true, site builds emit a serialized sidecar per page in this format (requires a
serialize
capability). |
remote |
boolean |
false |
When true, the
load
capability accepts
http(s)
URLs as sources. Remote content sources
must
name a remote-capable format explicitly. |
Two classes may claim the same extension
only with disjoint capabilities
— the registry build fails on an ambiguous
(extension, capability)
pair. A registry never claims
.json
.
Format capabilities
The block declares
what
the class handles; the class's
capability methods
declare
how
. Four roles belong to the
format
block:
| Role | Signature | Consumers |
|---|---|---|
parse |
(source, options?) → JxDocument |
compiler, server, Studio (open file) |
serialize |
(doc, options?) → string |
Studio (save), site build (export sidecars) |
discover |
(source, { baseDir }) → string[] |
content loading (list entry files) |
load |
(path, { schema, directiveOptions }) → ContentLoaderEntry[] |
content loading (parse one source) |
A format implements the subset it needs: a read-only format can ship
parse
without
serialize
(Studio then opens files in this format read-only in structural modes); a data-only format like
Csv
needs
discover
/
load
but has no reason to be a page format.
The
Markdown
class implements all four, plus the standard instance
resolve()
— so
{ "$prototype": "Markdown", "src": "./about.md" }
works as runtime state, satisfying the same
external class contract
as every other class.
How the pipeline dispatches
Hosts never hard-code file types. Each one builds a format registry from the enabled extensions' manifests and routes by extension:
Pages and components discovery — the site build and dev server glob for
.jsonplus every extension whose format declares the matchingdocumentKind, then callparseon non-JSON matches. This is why adding a Markdown page is just droppingpages/about.mdin a parser-enabled project.Content loading — a
contentsection entry names a format (or derives it from the source's file extension); the loader callsdiscoverto list entry files, thenloadper file, validating each entry against the content type's schema. See Content collections .Studio editing — opening a claimed file calls
parseto get the Jx tree the canvas edits; saving callsserialize. When a capability'stimingexcludes the browser, Studio round-trips through the dev server'sPOST /__studio/formatendpoint instead of importing the implementation ( Studio routes ).Export sidecars — with
exportTarget: true, the build serializes each page into the format next to its HTML output.
Studio hints
Format classes describe their Studio control surface declaratively in a top-level
$studio
block — Studio interprets this data generically and never hard-codes per-format element sets. From
Markdown.class.json
, abbreviated:
"$studio": {
"icon": "markdown",
"modes": ["edit", "design", "preview", "source"],
"documentMode": {
"default": "content",
"componentWhen": { "frontmatterKey": "tagName", "matches": ".+-.+" }
},
"newFileTemplate": "---\ntitle: Untitled\n---\n\n",
"elements": {
"block": ["h1", "h2", "h3", "p", "blockquote", "ul", "ol", "li", "pre", "…"],
"inline": ["em", "strong", "del", "code", "a", "img", "br"],
"void": ["hr", "br", "img"],
"textOnly": ["code"],
"nesting": {
"h1": { "block": false, "inline": true, "directive": false },
"ul": { "only": ["li"] },
"…": {}
}
}
}
icon— file icon in the Files panel;modes— which canvas modes the format supports.documentMode— whether files open as prose content or as components, with an optional frontmatter-based override (here: a hyphenatedtagNamemeans "this .md file defines a custom element").newFileTemplate— the seed content for New File in this format.elements— the allowlist and nesting constraints gating structural editing: which tags the element picker offers, what may nest where, which are void or text-only.
One more generic hint applies to any class, not just formats:
$studio.stateDefaults
— an object merged into state entries Studio creates for the prototype. The connector's
TableQuery
sets
{ "timing": "client" }
so Studio-created queries default to browser resolution.
Related
Tutorial: a TOML format extension — build a working format end to end
Capability methods — timing and the options contract
Content collections — the consumer of
discover/loadJx Markdown — what the reference format's dialect looks like