How to make sticky/fixed overlays work in Confluence Global Page Custom UI? (position:fixed not working)

Help needed: Fixed-position elements in Confluence Global Page Custom UI

Hey Forge community! :waving_hand:

I’m hoping someone has tackled this challenge before and can point me in the right direction.

What we’re building

We’ve built an interactive Domain Model visualization app using Forge Custom UI - think of it like a simplified Miro or a mind-map tool for software architecture. It shows our company’s domain hierarchy as an expandable tree graph that engineers can explore, with:

  • Zoom controls (bottom-left corner)

  • Component details panel (slides in from the right when you click a node)

  • Stats panel (team metrics, headcounts)

  • Pan/zoom on the SVG canvas

The app works beautifully in Compass using the compass:globalPage module - all the fixed-position overlay controls stick to the browser viewport exactly as expected.

The Problem

We want to offer the same visualization in Confluence using the confluence:globalPage module so non-technical stakeholders can explore the domain model too. But here’s where we’re stuck:

Confluence uses iframe-resizer to auto-size the Forge app iframe based on content height. This means our position: fixed elements (zoom controls, panels) are positioned relative to the iframe - which can be much taller than the visible browser viewport. The result? All our overlay controls end up “below the fold” and users have to scroll way down to find them.

In Compass, the iframe seems to match the viewport, so position: fixed works as expected. In Confluence Global Page, the iframe grows to content height, breaking the fixed positioning UX.

What we’ve tried (nothing worked)

  1. CSS constraints: height: 100vh; max-height: 100vh; overflow: hidden on html/body/root - iframe-resizer overrides it

  2. data-iframe-size attribute: Tried to tell iframe-resizer to use a specific element’s height - still measures DOM content

  3. window.parentIFrame.size() API: Called from child to request specific height - works momentarily but host-side iframe-resizer immediately overrides it

  4. Inline scripts to configure iframe-resizer: Blocked by CSP (no inline scripts allowed)

  5. confluence:fullPage module: Had React IntlProvider errors for non-admin users

We noticed the macro module supports viewportSize property, but confluence:globalPage doesn’t have this option in the manifest schema.

Questions for the community

  • Has anyone successfully used position: fixed overlays in a confluence:globalPage Custom UI app?

  • Is there a way to prevent iframe-resizer from auto-expanding the iframe height?

  • Any creative workarounds for sticky/fixed UI elements in Confluence Forge apps?

  • Should we be using a different module type altogether for this kind of full-page interactive visualization?

Technical details

  • Module: confluence:globalPage with Custom UI

  • Framework: React 18, vanilla SVG for the graph

  • Forge CLI: Latest version

  • Positioning strategy: position: fixed with bottom: 20px / right: 0 etc.

Any pointers would be hugely appreciated! :folded_hands:

Thanks!

This would be my related feature request:

Feature Request:
Add viewportSize property support to confluence:globalPage module

Summary

Add the viewportSize manifest property to the confluence:globalPage module, enabling developers to control the iframe sizing behavior for Custom UI apps.

Problem Statement

Currently, the confluence:globalPage module uses iframe-resizer to automatically size the Forge app iframe based on content height. This causes significant issues for apps that use position: fixed CSS for sticky UI elements (zoom controls, side panels, toolbars, etc.).

The core issue: position: fixed inside an iframe is relative to the iframe viewport, not the browser viewport. When iframe-resizer expands the iframe to match content height (which can be taller than the browser viewport), fixed-positioned elements end up “below the fold” - invisible to users without scrolling.

Use Case

We’re building an interactive graph visualization app (similar to Miro or Lucidchart) that displays in both Compass and Confluence. The app features:

  • Zoom controls (fixed bottom-left)

  • Details panel (fixed right side)

  • Stats panel (fixed right side)

In Compass (which doesn’t use iframe-resizer), these controls stick to the browser viewport as expected. In Confluence Global Page, the iframe grows to content height, pushing all fixed-position elements below the visible area.

Current Workarounds Attempted (All Failed)

  1. CSS height: 100vh; max-height: 100vh; overflow: hidden - iframe-resizer overrides this

  2. data-iframe-size attribute - iframe-resizer still measures DOM content

  3. window.parentIFrame.size() API - Host-side iframe-resizer overrides child requests

  4. Inline scripts to intercept iframe-resizer - Blocked by CSP

Proposed Solution

Add viewportSize property support to confluence:globalPage, consistent with the existing macro module:

modules:
  confluence:globalPage:
    - key: my-global-page
      title: My App
      route: my-app
      resource: my-resource
      viewportSize: xlarge  # NEW - 'small', 'medium', 'large', 'xlarge', or 'max'

With viewportSize set, the iframe would have a fixed height rather than auto-sizing to content, allowing position: fixed elements to work correctly.

Impact

This limitation affects any Confluence Global Page app that needs:

  • Sticky toolbars or navigation

  • Fixed-position control panels

  • Map/canvas/graph visualizations with overlay controls

  • Any UI pattern common in modern web applications

Alternatives Considered

  • Use only macro module: Macros support viewportSize, but Global Pages provide a better full-page app experience with dedicated navigation

  • Redesign UI without fixed positioning: Would require significant rework and result in inferior UX compared to the same app in other Atlassian products

References

Environment

  • Forge CLI version: Latest

  • App type: Custom UI

  • Products: Confluence Cloud

The only “solution” I found was from candid who posted this solution a couple years back: Sticky toolbar inside an Atlassian Connect iframe

But this isn’t ideal as there is considerable lag time from the user scrolling to the “stickied” element updating its position, resulting in a bad UX. We didn’t end up going with this route and just decided to forego stickied elements; though our app didn’t really need a stickied element anyways.

Thanks. I have for now abandoned the Confluence Global Page module. Too many UI-workarounds needed. Switched to the JIRA Global Page and my Compass app looked great right out of the gate - only adjustment I had to make was to remove a duplicate app title since JIRA displays the app name forcefully at the top.

A consistent experience between global page modules would be nice :wink: