Developer
News and Updates
Get Support
Sign in
Get Support
Sign in
DOCUMENTATION
Cloud
Data Center
Resources
Sign in
Sign in
DOCUMENTATION
Cloud
Data Center
Resources
Sign in
Forge modules
Automation modules
Bitbucket modules
Compass modules
Confluence modules
Dashboard modules (EAP)
Global module (EAP)
Jira modules
Jira Service Management modules
Customer Service Management modules
Jira Software modules
Rovo modules
Teamwork Graph modules
Last updated Jul 17, 2026

Macro

With the release of @forge/react version 11.0.0, enhancements have been made to the useConfig hook to improve performance in macro config apps when receiving configuration value changes.

Confluence macro config apps relying on the useProductContext hook or view.getContext() need to transition to the useConfig hook before upgrading to @forge/react version 11.0.0 or higher in order to properly access the latest values after the configuration updates.

Confluence macro config apps using the useConfig hook should upgrade to @forge/react version 11.0.0 for improved performance.

The macro module inserts dynamic content into the user interface via an editor. Editor macros are only compatible with the Atlassian editor. All cloud sites use the Atlassian editor by default.

The macro module works in Confluence, where the macro is inserted by typing / and selecting from the quick insert menu of the editor. The macro module is implemented by a Forge function.

Example of a macro

Manifest structure

1
2
modules {}
└─ macro []
   ├─ key (string) [Mandatory]
   ├─ resource (string) [Mandatory]
   ├─ render (string) [Optional]
   ├─ resolver {} [Optional]
   ├─ viewportSize (string) [Optional]
   ├─ title (string | i18n) [Mandatory]
   ├─ icon (string) [Optional]
   ├─ categories (string[]) [Optional]
   ├─ unlicesedAccess (List<string>) [Optional]
   ├─ description (string | i18n) [Optional]
   ├─ hidden (boolean) [Optional]
   └─ config (boolean | {} | config object) [Optional]
     ├─ icon (string) [Optional]
     ├─ title (string | i18n) [Optional]
     ├─ resource (string) [Mandatory]
     ├─ render (string) [Optional]
     ├─ viewportSize (string) [Optional]
     ├─ openOnInsert (boolean) [Optional]
     └─ parameters [] [Optional]
        ├─ identifier (string) [Mandatory]
        ├─ type (string) [Mandatory]
        └─ indexing {} [Optional]
           └─ enabled (boolean) [Optional]
   ├─ adfExport {} [Optional]
   ├─ layout (string) [Optional]
   └─ autoConvert [] [Optional]
     └─ matchers [] [Mandatory]
        └─ pattern (string) [Mandatory]

resources []
├─ key (string) [Mandatory]
└─ path (string) [Mandatory]

Properties

PropertyTypeRequiredDescription
key

string

Yes

A key for the module, which other modules can refer to. Must be unique within the manifest.

Regex: ^[a-zA-Z0-9_-]+$

resourcestringIf using Custom UI or modern versions of UI KitThe key of a static resources entry that your module will display. See resources for more details.
render'native'If using modern versions of UI KitIndicates the module uses UI Kit.
resolver{ function: string } or
{ endpoint: string }

Set the function property if you are using a hosted function module for your resolver.

Set the endpoint property if you are using Forge Remote to integrate with a remote back end.

viewportSize'small', 'medium', 'large' or 'xlarge'

Use viewportSize to pre-reserve the height in the editor before the app loads. Setting this prop disables auto-resizing. This is only supported for the main macro in Custom UI apps.

titlestring or i18n objectYes

The title of the macro. In Confluence, this is displayed in the editor.

The i18n object allows for translation. See i18n object.

iconstring

The icon displayed next to the title.


For Custom UI and UI Kit apps, the icon property accepts a relative path from a declared resource. Alternatively, you can also use an absolute URL to a self-hosted icon. See Icons for more information.

If no icon is provided, or if there's an issue preventing the icon from loading, a generic app icon will be displayed.

categoriesstring[]The categories of the macro. In Confluence, this is used for categorisation in the macro browser.
  • formatting
  • confluence-content
  • media
  • visuals
  • navigation
  • external-content
  • communication
  • reporting
  • admin
  • development
descriptionstring or i18n object

The description of the macro. In Confluence, this is displayed in the editor.

The i18n object allows for translation. See i18n object.

hiddenboolean

Defaults to false. When set to true, hides the macro from the quick insert menu and macro browser in Confluence. This prevents users from inserting new instances of the macro through these interfaces.

Existing macros on pages continue to render normally, even when this property is set to true.

configboolean, { function: string }, { openOnInsert: boolean } or config object

Set config to true if you are using classic macro configuration without needing openOnInsert.

Set config with the openOnInsert property if you are using classic macro configuration and need the openOnInsert feature. openOnInsert defaults to false.

Set config to the config object if you are using a custom macro configuration.

config.iconstring The icon displayed next to the title in the custom config modal.

For Custom UI and UI Kit apps, the icon property accepts a relative path from a declared resource. Alternatively, you can also use an absolute URL to a self-hosted icon. See Icons for more information.

If no icon is provided, or if there's an issue preventing the icon from loading, a generic app icon will be displayed.

config.titlestring or i18n objectA title for the config. If the viewport size is fullscreen*, then the title rendered in the modal header will be this title.
config.resourcestringRequired if using Custom UI or the latest version of UI Kit.A reference to the static resources entry that your context menu app wants to display. See resources for more details.
config.render'native'Yes for UI KitIndicates the module uses UI Kit.
config.viewportSize'small', 'medium', 'large', 'xlarge', 'max', 'fullscreen', 'resizable' (Preview) or { width: string; height: string } (Preview)The display size of resource. Can only be set if the module is using the resource property. For fullscreen viewports, the config.title and config.icon will be displayed in the header. Refer to resizable design guidelines for more direction. For the custom dimensions object, it accepts any common CSS size string for width and height,, e.g. '500px', '60vh'.
config.openOnInsertbooleanDefaults to false for classic configuration, defaults to true for custom configuration. An optional configuration to control if the classic configuration sidepanel or the custom configuration modal is automatically opened when first inserted.
config.parameters[parameter, ...]An optional list of parameters that describe the configuration values your macro expects. Supported for both classic configuration (using openOnInsert) and custom configuration (using the resource property).
config.parameters.identifierstringYes, if using parametersA unique identifier for the parameter. Must start with a letter and contain only alphanumeric characters, dashes, and underscores. Between 1 and 255 characters.
config.parameters.type'string', 'confluence-content', 'attachment' or 'spacekey'Yes, if using parametersThe type of the parameter value. This determines how Confluence stores and returns the value. See Config parameter types and value formats for the meaning of each type and its value format.
config.parameters.indexing{ enabled: boolean }Optional indexing configuration for the parameter.
config.parameters.indexing.enabledbooleanDefaults to false. When set to true, the macro parameter value is added to the Confluence search index.
adfExport{ function: string }

Defines how your macro appears when a Confluence page is exported.

Contains a function property which references a function module that returns the macro content in Atlassian document format.

The specified function can consume the exportType directly from the function's payload in order to specify different views per export type. The exportType can be one of pdf, word, or other. See this tutorial for more information.

The adfExport function is invoked once per macro instance during export operations. Pages with many macro instances can trigger a large number of invocations in a single export, potentially causing rate limiting and performance issues. Consider minimizing backend work within the function and informing customers about potential limitations when using many macros on pages that will be exported.

layout'block', 'inline' or 'bodied'

'block' type is used by default.

'inline' shows the element inline with existing text.

  • For UI Kit apps, inline macros dynamically resize to wrap the content.
  • Custom UI inline macros have a minimum rendered width of approximately 300px due to the browser's default iframe sizing. To allow your macro to render at a smaller width, set width: fit-content on the body element of your Custom UI app's HTML. See Notes on layout and sizing for details.

'bodied' sets the macro to have a rich text body.

  • This allows users to insert and edit rich content (such as images and tables) within the macro using the Confluence editor, and allows your app to insert a body using a custom editor.
  • Please see the link to the tutorial here.

autoConvertautoConvert object Inserts a macro into the editor when a recognised URL is pasted in by the user. See Macro autoconvert.
autoConvert.matchers[matcher, ...]Yes, if using autoConvertThe list of patterns that define what URLs should be matched.
autoConvert.matchers.patternstringYes, if using autoConvert A string that defines a specific URL pattern to be matched, using wildcards for variable parts of the URL, such as unique IDs.
  • Use multiple wildcards to match multiple sub-paths. Do not include all sub-paths with a single wildcard.
  • Ensure URLs do not contain whitespace unless it is URL encoded.
  • Wildcards cannot be used in place of a protocol. Custom URL Schemes are supported See examples
  • Maximum length of the pattern is 1024 characters.
emitsReadyEventbooleanNoDefaults to false. An optional configuration to notify Confluence that the macro will send a emitReadyEvent when it has completed loading and is ready for export or further processing. This should be used with view.emitReadyEvent(). See the view bridge function for more information.
unlicensedAccessList<string> A list of unlicensed user types that can access this module. Valid values are: unlicensed (Guests Users), and anonymous. For more information, see Access to Forge apps for unlicensed Confluence users.

i18n object

KeyTypeRequiredDescription
i18nstringYesA key referencing a translated string in the translation files. For more details, see Translations.

Dynamic module (Preview)

This module can also be declared as a dynamic module. However, this capability is currently available as a Forge preview feature.

For more details, see Dynamic Modules.

When you register a dynamic macro module, the data object uses the same properties as a static macro module in the manifest. The module key is generated server-side and returned in the create response; for updates, supply it in the URL path.

Code examples

The following examples show Dynamic Module implementations specific to this module. For more detailed information about the API used in these examples (including error handling information), see Dynamic Modules API.

Create a dynamic macro module

1
2
import { asApp } from "@forge/api";
const payload = {
  "type": "macro",
  "data": {
    "resolver": {
      "function": "resolver"
    },
    "resource": "main",
    "render": "native",
    "title": "Dynamic macro",
    "description": "A macro registered with the Dynamic Modules API",
    "autoConvert": {
      "matchers": [
        {
          "pattern": "https://www.example.com/*/about"
        }
      ]
    }
  }
}
const response = await asApp().requestAtlassian(`/forge/installation/v2/dynamic/module/`, {
  headers: {
    'Content-Type': 'application/json'
  },
  method: 'POST',
  body: JSON.stringify(payload),
});
const body = await response.text();
console.log(`Response: ${response.status} ${body}`);

Update a dynamic macro module

1
2
import { asApp } from "@forge/api";
const key = "macro-dynamic";
const payload = {
  "type": "macro",
  "data": {
    "resolver": {
      "function": "resolver"
    },
    "resource": "main",
    "render": "native",
    "title": "Updated dynamic macro",
    "description": "A macro updated with the Dynamic Modules API",
    "autoConvert": {
      "matchers": [
        {
          "pattern": "https://www.example.com/*/about"
        },
        {
          "pattern": "https://www.example.com/*/music"
        }
      ]
    }
  }
}
const response = await asApp().requestAtlassian(`/forge/installation/v2/dynamic/module/${key}`, {
  headers: {
    'Content-Type': 'application/json'
  },
  method: 'PUT',
  body: JSON.stringify(payload)
});
const body = await response.text();
console.log(`Response: ${response.status} ${body}`);

When you update the URL patterns for a dynamic macro module's autoconvert matchers, you may need to leave the Confluence editor, perform a hard refresh on the Confluence page view, and then re-open the editor before the updated patterns are applied.

Extension context

UI Kit and Custom UI

PropertyTypeDescription
typestringThe type of the module (macro).
content.idstringA string that represents the unique identifier of the content object.
content.type"page", "blogpost" or "space"A string that represents the type of the content object.
content.subtypestring or nullA string that represents the subtype of the content object. null is returned if subtype does not apply.
space.idstringA string that represents the unique identifier of the space object.
space.keystringA string that represents the unique key of the space object.
isEditingbooleanIndicates whether the macro is opened in the editor or not.
referencesReferenceEntity[]An array of reference entities (if any). Reference entities are a list of any other ADF nodes on the page that are referenced by this macro.
configobjectThe configuration parameters saved in this macro.
macro.bodyADF documentThe rich text body of the macro. Available for layout: bodied macros only.
autoConvertLinkstringThe link pasted by a user that has matched an AutoConvert app.
template.idstringA string that represents the unique identifier of the template. This value is only available when the macro is in a saved template.

Macro autoconvert

Macro autoconvert allows your app to automatically insert a macro into the editor when a user pastes a recognized URL. This is achieved by defining URL patterns in the manifest using the matchers property. These matchersare registered in the editor when the app is installed.

Example

1
2
modules:
  macro:
    - key: autoconvert-app
      resource: main
      render: native
      resolver:
        function: resolver
      title: Forge app for autoconvert
      description: Example for autoconvert manifest
      autoConvert:
        matchers:
          - pattern: https://www.example.com/*/about
          - pattern: https://www.example.com/*/music
          - pattern: https://*.example.com/*/movies/*
          - pattern: https://example.com/gifs/*/
          - pattern: http://*.example.com/media/*/.gif
          - pattern: customScheme://example:custom
          - pattern: customScheme:example:*
  function:
    - key: resolver
      handler: index.handler
resources:
  - key: main
    path: src/frontend/index.jsx
app:
  id: "<your app id>"

The URL patterns use wildcards to match parts of the URL that can vary, such as unique IDs. Wildcards are defined using *.

Use a new * for each segment in the URL you want a wildcard for. For example, https://www.example.com/* will match https://www.example.com/about but will not match https://www.example.com/about/contact. To match this path as well you need to include https://www.example.com/*/* as one of your matchers.

You'll need to define a separate matcher for each relevant internet protocol, such as http and https.

Creating custom URL schemes is also supported. For example, customScheme:* can be used to match any URL that starts with that custom scheme such as customScheme://example:custom. Any custom schemes will have to be registered on the system they will be used on, such as iOS, Windows or Android. Either :// or just : can be used as the initial separator in the URL scheme then : thereon.

Example patterns

Wildcard path
1
2
- "pattern": "https://www.example.com/*/about"
Wildcard in subdomain
1
2
- "pattern": "https://www.*.example.com/help"
Matching wildcard paths
1
2
- "pattern": "https://bitbucket.org/*/*/*"
Matching custom URL schemes
1
2
- "pattern": "customScheme:example:custom"
- "pattern": "customScheme://example:custom"
Matching custom URL scheme wildcard
1
2
- "pattern": "customScheme:example:*"
- "pattern": "customScheme://example:*"

When a pasted URL matches a defined pattern, the macro is created in the editor, and the URL is captured and inserted as a parameter into the macro body. This parameter can be accessed using the autoConvertLink property.

Macro custom configuration

Extension context in the macro editor

There are two additional extension context parameters available when you are in macro configuration editor context.

ParameterTypeDetails
macro.isConfiguringbooleantrue if the currently rendered resource is the config resource, false if it is the macro's default resource
macro.isInsertingbooleantrue if a new macro is being inserted, false if an existing macro is being edited

Config parameter types and value formats

Each entry in config.parameters declares a type that determines how Confluence stores the value and what format your app receives.

Values are always returned from useConfig() (and must be sent to view.submit()) as strings. For confluence-content (single and multiple) and all multi-value parameters, the string is a stringified JSON object or array that follows a special format described in the table below. For single attachment and spacekey values, the string is the raw filename or space key and does not need to be parsed.

Using confluence-content, attachment, or spacekey stores the value as a structured Confluence reference (rather than plain text), which lets Confluence automatically update the reference if the linked content is renamed or moved.

For example, this macro declares three config parameters, each with a unique identifier and a type:

1
2
modules:
  macro:
    - key: my-macro
      resource: main
      render: native
      resolver:
        function: resolver
      title: My Macro
      config:
        resource: config
        render: native
        parameters:
          - identifier: message
            type: string
          - identifier: targetPage
            type: confluence-content
          - identifier: relatedSpaces
            type: spacekey

The identifier you choose is the key you use to read the value back from useConfig():

1
2
import { useConfig } from "@forge/react";

const config = useConfig();
const message = config.message; // value of the "message" parameter
TypeMeaningForge format — singleForge format — multiple
stringA plain text value.The raw string, e.g. "my value".A JSON array string, e.g. ["a", "b"].
confluence-contentA reference to a Confluence page or blog post.A JSON object string. space-key is omitted when the content is in the current space; blog posts also include posting-day. See the confluence-content object reference for all fields.A JSON array string of objects.
[{"content-title":"page 1"},{"content-title":"page 2","space-key":"ENG"},{"content-title":"my blog","posting-day":"2026/05/25","space-key":"OTHER"}]
attachmentA reference to an attachment, by filename. This attachment is attached to the current content.A bare string (the filename) — not JSON.
"doc.pdf"
A JSON array string of filenames.
["file1.pdf", "file2.docx"]
spacekeyA reference to a Confluence space, by key.A bare string (the space key) — not JSON.
"OTHER"
A JSON array string of space keys.
["ENG", "OTHER"]

The Forge format — multiple column shows the runtime value format your app receives and sends (a stringified JSON array) — it is not a manifest declaration. You do not declare "multiple" in the manifest; a parameter accepts multiple values based on how the field is configured, and the value is then serialized as a JSON array string at runtime.

confluence-content object reference

Each confluence-content value (single, or each item in a multiple array) is a JSON object with the following fields:

FieldTypeRequiredDescription
content-titlestringYesThe title of the referenced page or blog post.
space-keystringNoThe key of the space containing the content. Omitted when the content is in the same space as the macro; if omitted, the current space is inferred.
posting-daystringNoThe publish date of a blog post, formatted as YYYY/MM/DD. Present only for blog posts — when this field is present the value is treated as a blog post reference; when it is absent, the value is treated as a page reference.

Example of a page reference (same space):

1
2
{ "content-title": "hello world" }

Example of a page reference in another space:

1
2
{ "content-title": "hello world", "space-key": "OTHER" }

Example of a blog post reference:

1
2
{
  "content-title": "my blog",
  "posting-day": "2026/04/27",
  "space-key": "OTHER"
}

For any value stored as JSON (all confluence-content values, and any multi-value parameter), parse the string before using it:

1
2
import { useConfig } from "@forge/react";

const config = useConfig();
// e.g. a `confluence-content` parameter named "targetPage"
const targetPage = JSON.parse(config.targetPage);
// { "content-title": "hello world", "space-key": "OTHER" }

Conversely, when you set one of these JSON-valued typed parameters (any confluence-content value, or any multi-value parameter), build the object (or array) and serialize it with JSON.stringify() before passing it to view.submit(). The value must be sent as a string:

1
2
import { view } from "@forge/bridge";

// e.g. a `confluence-content` parameter named "targetPage"
const targetPage = { "content-title": "hello world", "space-key": "OTHER" };

view.submit({
  config: {
    targetPage: JSON.stringify(targetPage),
  },
});

Single attachment and spacekey values are the exception — send them as the raw filename or space key string, without JSON.stringify(). For plain (non-typed) config parameters, you can pass values in their native form (strings, numbers, booleans, objects, and arrays); see Supported config payload format.

Options for submitting the configuration

This table details the options supported by view.submit() in the context of custom macro configuration.

ParameterTypeRequiredDetailsCode
configConfig payloadYesSets the config properties of the macro.
1
2
view.submit({
  config: {
    param1: "test",
    param2: [1, 2, 3]
  }
})
bodyADF documentNoSets the rich text body of the macro. Can only be used with layout: bodied macros.
1
2
view.submit({
  config: {},
  body: {
    type: "doc",
    version: 1,
    content: [
      // ADF content
    ]
  }
})
keepEditingbooleanNoDefaults to false, which automatically closes the config modal on submit. Set this to true to keep the modal open.
1
2
view.submit({
  config: {},
  keepEditing: true
})

Supported config payload format

The config payload only supports values that can be serialised to JSON.

The following types are allowed on the payload:

  1. undefined
  2. string
  3. number
  4. boolean
  5. object (can contain any of the allowed types, including nested objects)
  6. array (can contain strings, numbers, booleans, and objects; all items in the array must be of the same type).

The following types are not allowed:

  • Nested arrays (arrays as direct children of arrays)
  • null
  • Any data types that are not serializable to JSON (e.g. Map, Set, etc.)

If you want greater control over the storage format of your configuration, such as being able to store nested arrays and nulls, we recommend serializing your configuration to JSON upfront, and storing it as a string.

Error code guide

This table details the possible error codes that may be thrown by view.submit():

Error codeDetails
INVALID_PAYLOADThe top-level parameter passed to view.submit() must be an object.
INVALID_CONFIGThe config prop provided must be an object that is compliant with the config payload format above.
INVALID_EXTENSION_TYPEWhen providing a body, the macro must be a rich text macro (layout: "bodied").
INVALID_BODYThe provided body is not a valid ADF document node.
MACRO_NOT_FOUNDThe macro that you are attempting to update no longer exists. It may have been deleted by another user editing the page.

Notes on layout and sizing

Inline macro width (Custom UI)

Custom UI inline macros are rendered inside an <iframe>. All major browsers apply a default minimum width of 300px to <iframe> elements, which means your inline macro will render at a minimum of approximately 300px wide even if the content inside is smaller.

To override this and allow the macro to shrink to fit its content, add the following CSS to your Custom UI app. The recommended approach is to add it in a <style> tag or an external stylesheet in your index.html:

1
2
<style>
  body {
    width: fit-content;
  }
</style>

This tells the browser to size the body to its content rather than expanding to the iframe default width. The fit-content value is supported across all browsers that Forge targets.

If you apply these styles using a style attribute directly on the <body> element (for example, <body style="width: fit-content">), you must also declare the unsafe-inline permission in your manifest.yml, as inline styles are blocked by the default Content Security Policy:

1
2
permissions:
  content:
    styles:
      - "unsafe-inline"

Using a <style> tag or external stylesheet does not require this permission.

This limitation only applies to Custom UI apps. UI Kit inline macros automatically resize to wrap their content without any additional configuration.

Tutorials

Rate this page: