Stand with Ukraine 🇺🇦
Products
Services
Blog
About Us

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**
image-20260622-125203.png


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>