Last updated Nov 11, 2024

Add custom configuration to a macro

This section describes a Forge preview feature. Preview features are deemed stable; however, they remain under active development and may be subject to shorter deprecation windows. Preview features are suitable for early adopters in production environments.

We release preview features so partners and developers can study, test, and integrate them prior to General Availability (GA). For more information, see Forge release phases: EAP, Preview, and GA.

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.

Before you begin

Make sure you have the following:

  • Confluence macro created using UI Kit or Custom UI.
  • The latest version of Forge CLI. To update your CLI version, run npm install -g @forge/cli@latest on the command line.

Step 1: Configure the manifest

First, set the config property to the config object in the manifest.yml file.

1
2
macro:
  - key: my-macro
    ...
    config:
      resource: macro-config
      render: native # Only for UI Kit
      viewportSize: max # Optional
      title: Config # Optional

Step 2: Declare resources for the custom editor

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.

For UI Kit, we recommend the path src/frontend/config.jsx

For custom UI, we recommend the path static/config/build

1
2
resources: 
  - key: macro-config
    path: relative/path/to/resource/file

Step 3: Create the configuration resource

Define the config resource at the path specified in step 2.

For UI Kit, add the following imports to the file src/frontend/config.jsx.

1
2
import React, { useState, useEffect } from 'react';
import ForgeReconciler, { Button, Label, SectionMessage, Stack, Textfield } from '@forge/react';
import { view } from '@forge/bridge';

Next, in the same file, define the config function. We will write the functionality for the submit function in step 4.

1
2
const Config = () => {
  const [value, setValue] = useState('');
  const {
    error,
    message,
    submit
  } = useSubmit();
  
  useEffect(async () => {
    const context = await view.getContext();
    setValue(context.extension?.config?.myField);
  }, []);

}

Next, define the structure of the modal in the return statement for the config function. This can be customised however you wish.

Here is an example of a basic modal for UI Kit.

1
2
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>
);

For UI Kit, at the bottom of the file, use ForgeReconciler to render the modal.

1
2
ForgeReconciler.render(
  <React.StrictMode>
    <Config />
  </React.StrictMode>
);

Step 4: Persist changes using view.submit()

Next, you need to create a method to submit the updated configuration. You can achieve this with the view.submit() method. If you wish to close the editor without making any changes, use view.close instead. Add the following code into your config resource file from step 3.

1
2
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
  };
};

view.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.

Step 5: Show changes on app

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
2
import React from 'react';
import ForgeReconciler, { useProductContext, CodeBlock, Text } from '@forge/react';

const App = () => {
  const context = useProductContext();
  const config = context?.extension?.config;

  return (
    <>
      <Text>Macro configuration data:</Text>
      <CodeBlock language="json" text={JSON.stringify(config, null, 2)} />
    </>
  );
};

ForgeReconciler.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Rate this page: