JSON and Markdown in User Macros
User Macro for Confluence Cloud allows for formats beyond HTML rendering to handle structured data (JSON), Markdown content, and Atlassian Document Format (ADF / atlas_doc_format).
Handling JSON and Markdown in User Macro Templates
JSON in Velocity Templates
Velocity can safely handle JSON as plain text.
Example: embedding JSON from backend
#set($data = {
"title": "Build Status",
"items": [
{ "name": "API", "status": "ok" },
{ "name": "UI", "status": "warning" }
]
})
$data.title // output: Build Status
Parsing JSON in client-side JS (Velocity object → JS object)
Most practical approach in User Macros is to let Velocity inject values directly into JavaScript, avoiding manual JSON parsing when structure is simple.
#set($data = {
"title": "System",
"count": 5
})
<div id="output"></div>
<script>
const data = {
title: "$data.title",
count: $data.count
};
document.getElementById("output").innerText =
data.title + " - " + data.count;
</script>
Handling JSON inside macro body
If users paste JSON into macro body:
<pre id="rawJson">
$body
</pre>
<script>
const raw = document.getElementById("rawJson").innerText;
try {
const parsed = JSON.parse(raw);
console.log(parsed);
} catch (e) {
console.log("Invalid JSON");
}
</script>
// Input: {
"employee": {
"name": "Jane Doe",
"age": 30,
"is_active": true,
"skills": [
"JavaScript",
"Python",
"JSON"
],
"manager": null,
"contact_info": {
"email": "jane.doe@example.com",
"phone": "+380501234567"
}
}
}
// Output: {employee={name=Jane Doe, age=30, is_active=true, skills=[JavaScript, Python, JSON], manager=null, contact_info={email=jane.doe@example.com, phone=+380501234567}}}
Markdown handling
Markdown is not rendered natively in Confluence macros, so it must be parsed in JS.
{"timestamp":1792799681390,"msg":"A unknown Exception Occurred","errorMsg":"The provided AtlassianHostUser did not specify a user to act as.","code":"500"}Read more about body types here
Example: Markdown input in macro body
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<div id="mdContainer">
$body
</div>
<script>
const md = document.getElementById("mdContainer").innerText;
const html = marked.parse(md);
document.getElementById("mdContainer").innerHTML = html;
</script>
Example Markdown input and output
# Title - Item 1 - Item 2 **Bold text**
Working with ADF (Atlassian Document Format)
Example ADF structure
ADF is deeply nested JSON:
{
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Hello ADF"
}
]
}
]
}
Accessing ADF in User Macros
Fetch ADF via AP.request
<div id="adfOutput"></div>
<script>
AP.request('/wiki/api/v2/pages/335151106?body-format=atlas_doc_format')
.then(res => JSON.parse(res.body))
.then(data => {
const doc = data.body.atlas_doc_format.value;
console.log(doc);
document.getElementById("adfOutput").innerText =
JSON.stringify(doc, null, 2);
});
</script>
Fetching ADF server-side (Velocity-first approach)
ADF can be fetched directly in Velocity using $ConfluenceManager.
This is faster, avoids client-side complexity, and keeps logic on the server.
## Fetch page ADF on server side
#set($response = $ConfluenceManager.get(
"/wiki/api/v2/pages/${child.id}?body-format=atlas_doc_format"
))
## Extract ADF payload
#set($adf = $response.body.atlas_doc_format.value)
<div id="adfContainer"></div>
<script>
const adf = $adf;
console.log(adf);
</script>