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.
Configuration allows you to customize what the macro displays by adjusting settings in a form. To access these settings, you need to go into the edit mode for the macro, as demonstrated below. This gives you the ability to customize the macro's output according to your preferences.
Custom configuration can be used for more complex use cases, such as when you need to use specialised input components, or want more control over the rendering experience of configuring a macro. It also allows saving arbitrary configuration at runtime, instead of requiring you to predefine all possible configuration fields.
Custom configuration supports both UI Kit and custom UI.
You can add simple configuration to a macro using UI Kit components, as described here.
You can use rich text bodied macros, as described here.
You can also see a sample implementation of a macro with custom configuration in the rich-text-custom-config-macro sample app.
Make sure you have the following:
npm install -g @forge/cli@latest
on the command line.First, set the config
property to the config object
in the manifest.yml
file.
If you already have a macro in your manifest.yml
, add the config
section to your existing macro definition:
Before:
1 2modules: macro: - key: my-macro resource: main render: native resolver: function: resolver title: My Macro
After:
1 2modules: macro: - key: my-macro resource: main render: native resolver: function: resolver title: My Macro config: resource: macro-config render: native # Only for UI Kit viewportSize: max # Optional title: Config # Optional
If you're creating a new macro, use this complete structure:
1 2modules: macro: - key: my-macro resource: main render: native resolver: function: resolver title: My Macro config: resource: macro-config render: native # Only for UI Kit viewportSize: max # Optional title: Config # Optional function: - key: resolver handler: index.handler resources: - key: main path: src/frontend/index.jsx
Next, define the resources
property in the manifest.yml
file according to the
Resources page.
The key
prop should match the resource
name in the config
object, and the path
prop
should point to the config
resource file that we will create in step 3.
Add the macro-config
resource to your existing resources
section:
1 2resources: - key: main path: src/frontend/index.jsx - key: macro-config path: src/frontend/config.jsx
The key
value macro-config
must match the resource
value in the config
object from Step 1.
Define the config
resource at the path specified in step 2.
Create the file src/frontend/config.jsx
with the following complete code:
1 2import React, { useState, useEffect } from 'react'; import ForgeReconciler, { Button, Label, SectionMessage, Stack, Textfield, useConfig } from '@forge/react'; import { view } from "@forge/bridge"; // Submit functionality - handles configuration persistence const useSubmit = () => { const [error, setError] = useState(); const [message, setMessage] = useState(''); const submit = async (fields) => { const payload = { config: fields }; try { await view.submit(payload); setError(false); setMessage(`Submitted successfully.`); } catch (error) { setError(true); setMessage(`${error.code}: ${error.message}`); } }; return { error, message, submit }; }; // Main configuration component const Config = () => { const [value, setValue] = useState(''); const config = useConfig(); const { error, message, submit } = useSubmit(); useEffect(() => { setValue(config?.myField); }, [config?.myField]); return ( <Stack space="space.200"> <Label labelFor="myField">Config field:</Label> <Textfield id="myField" value={value} onChange={(e) => setValue(e.target.value)} /> <Button appearance="subtle" onClick={() => view.close()}> Close </Button> <Button appearance="primary" onClick={() => submit({ myField: value })}> Submit </Button> {typeof error !== 'undefined' && ( <SectionMessage appearance={error ? 'error' : 'success'}>{message}</SectionMessage> )} </Stack> ); }; // Render the configuration modal ForgeReconciler.render( <React.StrictMode> <Config /> </React.StrictMode> );
You will not be able to close the configuration modal by pressing Escape
or by clicking outside the modal.
In UI Kit, we provide a modal header that contains a close button, and users will be able to close the configuration modal that way. The close button does not call view.submit()
- you should provide submit functionality in the configuration resource as shown above.
The configuration persistence functionality is already included in the complete config.jsx
file from Step 3.
The useSubmit
function handles configuration persistence using the view.submit() method. Here's how it works:
submit
function creates a payload with { config: fields }
view.submit(payload)
saves the configurationview.submit(payload)
: Saves configuration and closes the modalview.close()
: Closes the modal without saving (cancels macro insert)The function includes comprehensive error handling that:
SectionMessage
componentview.submit()
supports more options than just config for updating the configuration. See
Options for submitting the configuration
for the full list of options.
To interpret the different error codes returned from view.submit()
, see Error code guide for the full list.
Before proceeding to Step 5, validate that your configuration is working correctly:
forge deploy
to deploy your changesforge install --upgrade
if you've changed permissions/
commandIf you encounter errors during testing, check the browser console for detailed error messages and refer to the Troubleshooting section below.
Finally, we can show the changes submitted from the config
on our app.
Place the following code into the src/frontend/index.jsx
file.
For UI Kit, at the bottom of the file, use ForgeReconciler
to render the modal.
1 2import React from 'react'; import ForgeReconciler, { useConfig, CodeBlock, Text } from '@forge/react'; const App = () => { const config = useConfig(); return ( <> <Text>Macro configuration data:</Text> <CodeBlock language="json" text={JSON.stringify(config, null, 2)} /> </> ); }; ForgeReconciler.render( <React.StrictMode> <App /> </React.StrictMode> );
Issue | Solution |
---|---|
Custom macro editor does not launch | Check if the custom macro editor resource is defined correctly in the manifest. |
Custom macro editor closes automatically | Check the custom macro editor resource for any view.close() calls. You may need to provide the keepEditing parameter in view.submit() , which will then keep the editor open on submit. |
Payload does not save | Check view.submit() error codes. Ensure that the payload abides by the supported payload format. |
Editor modal does not close when overlay is clicked | Clicking the modal background once the resource has loaded will not close the modal. However, if the resource is still loading, the user is able to click the background to close the modal. For a custom UI editor resource, the developer must provide a UI element that calls view.close() to allow the user to close the modal. |
Error in browser console: ForgeReconciler.addConfig() cannot be called from within a custom config resource | Check the custom editor resource for ForgeReconciler.addConfig() call(s) and remove them. Adding classic UI Kit config is not supported in custom configuration resources. |
Macro is not inserted onto page after I close the custom macro editor | Make sure you use view.submit() to close the macro editor if you want the macro inserted onto the page. Using view.close() will cancel the macro insert. |
Rate this page: