Stand with Ukraine 🇺🇦

Navigate Through Page Tree Using Sticky Buttons — Confluence Cloud Macro

This Confluence user macro uses the Confluence Cloud REST API to fetch the current page details, its ancestors, and direct children, then computes a depth-first “previous” and “next” page by combining child, sibling, and ancestor-sibling relationships. The macro resolves target pages to their web UI URLs and renders compact circular AUI-styled buttons that stick to the bottom-right of the viewport, dynamically adjusting position based on whether one or both navigation buttons are visible.

Try for free

User Parameters

This macro comes without configurable user parameters.

Template

## Styles
#set($navBgColor = "transparent")
#set($navWidth = "4em")
#set($navTop = "92%")
#set($navRight = "3.7%")

## Max depth for child pages when finding the "deepest leaf" for BACK
#set($maxDepthDown = 20)

## PAGE CONTEXT (current, parent, ancestors)
## Ancestors: root first, direct parent last (may be null/empty)
#set($ancestors = $ConfluenceManager.get("/wiki/api/v2/pages/${page.id}/ancestors?limit=50").results)

## FORWARD NAVIGATION
## 1) First child
## 2) Next sibling of current or any ancestor
#set($forwardHref = "")

## --- FORWARD STEP 1: first child of current page ---
#set($children = $ConfluenceManager.get("/wiki/api/v2/pages/${page.id}/direct-children?limit=250").results)
#set($firstChildId = "")

#if($children)
  #foreach($child in $children)
    #if($child.type == "page")
      #set($firstChildId = $child.id)
      #break
    #end
  #end
#end

#if($firstChildId != "")
  #set($nextPage = $ConfluenceManager.get("/wiki/api/v2/pages/${firstChildId}"))
  #if($nextPage._links && $nextPage._links.webui)
    #set($forwardHref = "${baseUrl}${nextPage._links.webui}")
  #end
#else
  ## --- FORWARD STEP 2: next sibling at this level or above ---
  #set($nextPageId = "")

  ## 2a) Next sibling of current page
  #if($page.parentId)
    #set($siblings = $ConfluenceManager.get("/wiki/api/v2/pages/${page.parentId}/direct-children?limit=250").results)
    #set($afterCurrent = false)

    #if($siblings)
      #foreach($sibling in $siblings)
        #if($sibling.type == "page")
          #if($afterCurrent && $nextPageId == "")
            #set($nextPageId = $sibling.id)
          #end
          #if($sibling.id == $page.id)
            #set($afterCurrent = true)
          #end
        #end
      #end
    #end
  #end

  ## 2b) If still none, next sibling of ancestors (nearest → farthest)
  #if($nextPageId == "" && $ancestors)
    #set($ancestorCount = $ancestors.size())

    #foreach($ignored in $ancestors)
      #if($nextPageId == "")
        ## Walk from direct parent up toward root using reverse index
        #set($reverseIndex = $ancestorCount - $foreach.count)
        #set($ancestor = $ancestors.get($reverseIndex))

        ## Skip top ancestor (no parent above it)
        #if($reverseIndex > 0)
          #set($parentIndex = $reverseIndex - 1)
          #set($ancestorParent = $ancestors.get($parentIndex))
          #set($ancestorParentId = $ancestorParent.id)

          #set($ancestorSiblings = $ConfluenceManager.get("/wiki/api/v2/pages/${ancestorParentId}/direct-children?limit=250").results)
          #set($afterAncestor = false)

          #if($ancestorSiblings)
            #foreach($sibling2 in $ancestorSiblings)
              #if($sibling2.type == "page")
                #if($afterAncestor && $nextPageId == "")
                  #set($nextPageId = $sibling2.id)
                #end
                #if($sibling2.id == $ancestor.id)
                  #set($afterAncestor = true)
                #end
              #end
            #end
          #end
        #end
      #end
    #end
  #end

  ## Resolve nextPageId to URL
  #if($nextPageId != "")
    #set($nextPage = $ConfluenceManager.get("/wiki/api/v2/pages/${nextPageId}"))
    #if($nextPage._links && $nextPage._links.webui)
      #set($forwardHref = "${baseUrl}${nextPage._links.webui}")
    #end
  #end
#end

## BACKWARD NAVIGATION (←)
## prev(page) =
##   - deepest leaf under previous sibling, if it exists
##   - otherwise parent
#set($backHref = "")
#set($backTargetId = "")

## --- BACK STEP 1: previous sibling of current page ---
#set($prevSiblingId = "")

#if($page.parentId)
  #set($backSiblings = $ConfluenceManager.get("/wiki/api/v2/pages/${page.parentId}/direct-children?limit=250").results)

  #if($backSiblings)
    #foreach($sibling in $backSiblings)
      #if($sibling.type == "page")
        #if($sibling.id == $page.id)
          #break
        #else
          ## Last page before current in this list
          #set($prevSiblingId = $sibling.id)
        #end
      #end
    #end
  #end
#end

## --- BACK STEP 2: deepest leaf under previous sibling or parent ---
#if($prevSiblingId != "")
  ## Drill down into previous sibling's subtree to the deepest leaf
  #set($backTargetId = $prevSiblingId)

  #foreach($depth in [1..$maxDepthDown])
    #set($downChildren = $ConfluenceManager.get("/wiki/api/v2/pages/${backTargetId}/direct-children?limit=250").results)
    #set($lastChildId = "")

    #if($downChildren)
      #foreach($child in $downChildren)
        #if($child.type == "page")
          #set($lastChildId = $child.id)
        #end
      #end
    #end

    #if($lastChildId != "")
      #set($backTargetId = $lastChildId)
    #else
      #break
    #end
  #end
#elseif($page.parentId)
  ## No previous sibling → use parent as previous page
  #set($backTargetId = $page.parentId)
#end

## Resolve backTargetId to URL
#if($backTargetId != "")
  #set($backTargetPage = $ConfluenceManager.get("/wiki/api/v2/pages/${backTargetId}"))
  #if($backTargetPage._links && $backTargetPage._links.webui)
    #set($backHref = "${baseUrl}${backTargetPage._links.webui}")
  #end
#end

## Button positions
#set($hasBack    = false)
#set($hasForward = false)

#if($backHref != "")
  #set($hasBack = true)
#end

#if($forwardHref != "")
  #set($hasForward = true)
#end

#if($hasBack && $hasForward)
  #set($navRight = "5.3%")
#elseif($hasBack || $hasForward)
  #set($navRight = "3.75%")
#end

## RENDER NAVIGATION UI
<div class="aui-dialog2 aui-layer"
  style="
  top: $navTop;
  height: 80em;
  right: $navRight;
  display: block;
  visibility: hidden;
  z-index: 10;
  ">
  <div style="
    padding: 0.4em;
    float: right;
    width: $navWidth;
    background-color: $navBgColor;
    height: auto;
    visibility: visible;
    overflow: hidden;
    text-align: center;
    ">

#if($backHref != "")
    <a href="$backHref"
      title="Previous page"
      style="
      z-index: 9999;
      text-decoration: none;
      color: var(--ds-text-subtle, #44546F);
      border: 1px solid grey;
      outline: none;
      border-radius: 50%;
      display: inline-block;
      width: 25px;
      height: 25px;
      text-align: center;
      font-size: 16px;
      margin-right: 4px;
      ">
      &larr;
    </a>
#end

#if($forwardHref != "")
    <a href="$forwardHref"
      title="Next page"
      style="
      z-index: 9999;
      text-decoration: none;
      color: var(--ds-text-subtle, #44546F);
      border: 1px solid grey;
      outline: none;
      border-radius: 50%;
      display: inline-block;
      width: 25px;
      height: 25px;
      text-align: center;
      font-size: 16px;
      ">
      &rarr;
    </a>
#end

  </div>
</div>