Last updated Oct 30, 2024

Add configuration to a macro with UI Kit

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.

Example of configuring a Forge macro

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.
  • The latest version of UI Kit. To update your version, navigate to the app's top-level directory, and run npm install @forge/react@latest --save on the command line. If your macro uses Custom UI, navigate to the resource directory and run the same command.

Add configuration to the Confluence macro module

In this tutorial, we will add fun facts about pets which are passed through configuration we create. We recommend you to change the values as listed in the below manifest.yml file.

Make sure you run forge login before you creating your app. For more information on how to log in to Forge, see Log in with an Atlassian API token.

Create an app using a template.

  1. Navigate to the directory where you want to create the app.

  2. Create your app by running:

    1
    2
    forge create
    
  3. Enter a name for the app.

  4. Select the UI Kit category from the list.

  5. Select the confluence-macro template from the list.

  6. Change to the app subdirectory to see the app files

To add configuration to the Confluence macro module:

  1. In the app’s top-level directory, open the manifest.yml file.
  2. Add the config property with the value set to true.

After doing this, your manifest.yml should look like the following, with your own values for each of the properties.

1
2
modules:
  macro:
    - key: pet-facts
      resource: main
      render: native
      resolver:
        function: resolver
      title: Pet
      config: true
  function:
    - key: resolver
      handler: index.handler
resources:
  - key: main
    path: src/frontend/index.jsx
app:
  id: "<your app id>"

UI Kit

Create the configuration

You can create a new function component that will return the configuration (Config) components. The config components must be selected from the following list:

Configuration is stored in key-value pairs corresponding to each form component.

We recommend clearing the src/frontend/index.jsx file and replacing it with the provided code for error-free results.

  1. In your src/frontend/index.jsx file, create a function component that constructs the configuration of the UI Kit components you're using:
1
2
import React from 'react';
import { Label, Textfield } from '@forge/react';

const Config = () => {
  return (
    <>
      <Label>Pet age</Label>
      <Textfield name="age" />
    </>
  );
};

The use of Label in the macro configuration is different from that of other extension points. It does not use the labelFor and id properties and will instead be rendered in the order specified in the app. See the Label in the macro configuration documentation for more details.

  1. At the bottom of the src/frontend/index.jsx file, call the addConfig method on ForgeReconciler with your config element. Ensure you have ForgeReconciler imported at the top.
1
2
import React from 'react';
import ForgeReconciler, { Label, Textfield } from '@forge/react';

const Config = () => {
  return (
    <>
      <Label>Pet age</Label>
      <Textfield name="age" />
    </>
  );
};

const App = () => {
  ...
}

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

ForgeReconciler.addConfig(<Config />);

Storing sensitive information

  • Use config to store general data, but not sensitive information.
  • The config data is stored in plaintext, so other users and apps can access and modify the values.
  • Consider using the encryption features of Forge Storage to store sensitive data.

Removing config fields

  • If you remove a field from the config definition component, the next time a user edits and saves the macro config, the value stored in that field will also be removed from the stored data.
  • It can only be recovered if the user restores an old version of the page prior to the edit.

Note: Changing the name property of a field is treated as removing the field and adding a new one.

Use the configuration

You access the config for a macro in your app code with the getContext method. When this method resolves, it returns the product context, which will include the key-value pairs set in the config. The key is the name property on the form component in configuration, and the value is what the user enters.

In your src/frontend/index.jsx file:

  1. Import useState and useEffect.
1
2
import React, { useState, useEffect } from 'react'; 
// useState: Adds and updates local state to a component. 
// useEffect: Executes a specific function whenever any value in its dependency array (the second argument of the hook) changes.
  1. Import view from @forge/bridge in your app code.
1
2
import { view } from '@forge/bridge';
  1. Add Label, Text, Textfield components.
1
2
import ForgeReconciler, { Label, Text, Textfield } from '@forge/react';
  1. Create a component called App and call the getContext method in the useEffect to get the product context.
1
2
const Config = () => {
...
};

const App = () => {
  const [context, setContext] = useState(undefined);

  useEffect(() => {
    view.getContext().then(setContext);
  }, []);

  return (
    <>
      <Text>Hello World</Text>
    </>
  );
};
  1. Access the configuration data by key within the App component. Add the second text component with age to the return statement.
1
2
const App = () => {

  ...

  const config = context?.extension.config;
  const age = config?.age;

  return (
    <>
      <Text>Hello World</Text>
      <Text>
        {age || 'Fetching config...'}
      </Text>
    </>
  );
}

The index.jsx file should look like this:

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

const Config = () => {
  return (
    <>
      <Label>Pet age</Label>
      <Textfield name="age" />
    </>
  );
};
const App = () => {
  const [context, setContext] = useState(undefined);

  useEffect(() => {
    view.getContext().then(setContext);
  }, []);
  const config = context?.extension.config;
  const age = config?.age;

  return (
    <>
      <Text>Hello World</Text>
      <Text>
        {age || 'Fetching config...'}
      </Text>
    </>
  );
};
ForgeReconciler.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

ForgeReconciler.addConfig(<Config />)

We recommend you add sensible defaults for each of your configuration values.

If it's more sensible for the macro to not have default configuration values, we recommend you display a section message with the appropriate instructions, as shown below:

1
2
import ForgeReconciler, { Label, Text, Textfield, SectionMessage } from '@forge/react'; 
//Add SectionMessage to the import statement

<SectionMessage title="You need to configure this macro" appearance="warning">
  <Text>
    While editing the page, select the macro, and click on the pencil icon
    to display configuration options.
  </Text>
</SectionMessage>

You can also add a state to only display the section message if the macro has not already been configured; otherwise, it will always show on the macro.

Custom UI

Create the configuration

You can create a new function component that will return the configuration (Config) components. The config components must be selected from the following list:

Configuration is stored in key-value pairs corresponding to each form component.

Make sure you run forge login before you creating your app. For more information on how to log in to Forge, see Log in with an Atlassian API token.

Create an app using a template.

  1. Navigate to the directory where you want to create the app.

  2. Create your app by running:

    1
    2
    forge create
    
  3. Enter a name for the app.

  4. Select the Custom UI category from the list.

  5. Select the confluence-macro template from the list.

  6. Change to the app subdirectory to see the app files

This assumes you are using a Custom UI template with the resource in static/hello-world. If you are not using a template, adjust the paths as needed.

  1. In a static/hello-world/src/Config.js file, create a function component that constructs the configuration of the UI Kit components you're using:
1
2
import React from 'react';
import { Label, Textfield } from '@forge/react';

const Config = () => {
  return (
    <Label>Pet Age</Label>
    <Textfield name="age" />
  );
};

export default Config;
  1. In the static/hello-world/src/index.js file, call the addConfig method on ForgeReconciler with your config element. Ensure you have ForgeReconciler imported at the top.
1
2
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import Config from './Config';
import ForgeReconciler from "@forge/react";

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

ForgeReconciler.addConfig(<Config />);

Use the configuration

To retrieve the configuration, follow the same steps as above but in your static/hello-world/src/App.js file instead. You can then use the configuration values in your app.

1
2
import React, { useState, useEffect } from 'react';
import { view } from "@forge/bridge";

const App = () => {
  const [context, setContext] = useState(undefined);

  useEffect(() => {
    view.getContext().then(setContext);
  }, []);

  const config = context?.extension.config;
  const age = config?.age;

  return (
    <>
      <div>Hello World</div>
      <div>
        {age || 'Fetching config...'}
      </div>
    </>
  );
}

export default App;

Add default pet configuration

In this example, by default, the macro displays Unnamed Pet is 0 years old.

Create a default configuration for the pet's name and age, and then add defaultConfig variable to const config = context?.extension.config

In your src/frontend/index.jsx file outside the Config and App components, add:

1
2
const defaultConfig = {
  name: 'Unnamed Pet',
  age: '0'
};

Then replace the const config inside the App component with the following:

1
2
 const config = context?.extension.config || defaultConfig;

Add dynamic input values for the pet's name and age.

Now that we have created configurations (name and age) for your macro, we can now enhance this feature to include dynamic input for the pet's name and age.

For example, if a user submits the name Fluffy and age 2, the macro displays Fluffy is 2 years old.

In your index.jsx file:

Add function that defines the config UI for the pet's name and age

1
2
const Config = () => {
  return (
    <>
      <Label>Pet name</Label>
      <Textfield name="name" defaultValue={defaultConfig.name} />
      <Label>Pet age</Label>
      <Textfield name="age" defaultValue={defaultConfig.age} />
    </>
  );
};

Display the pet's name and age using the configuration values

In the return statement of the App component:

Replace

1
2
<Text>Hello World</Text>
<Text>
  {age || 'Fetching config...'}
</Text>

with

1
2
return (
  <Text>{config.name} is {config.age} years old.</Text>
);

Your main file containing the application's root component (here index.jsx) should look like this:

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

const defaultConfig = {
  name: 'Unnamed Pet',
  age: '0'
};

// Function that defines the configuration UI for the pet's name and age
const Config = () => {
  return (
    <>
      <Label>Pet name</Label>
      <Textfield name="name" defaultValue={defaultConfig.name} />
      <Label>Pet age</Label>
      <Textfield name="age" defaultValue={defaultConfig.age} />
    </>
  );
};

const App = () => {
  const [context, setContext] = useState(undefined);
  // Retrieve the context
  useEffect(() => {
    view.getContext().then(setContext);
  }, []);
  
  const config = context?.extension.config || defaultConfig;
  // Displaying the pet's name and age using the configuration values. SectionMessage component is optional.
  return (
    <>
      <Text>{config.name} is {config.age} years old.</Text>
      <SectionMessage title="You need to configure this macro" appearance="warning"> 
        <Text>
          While editing the page, select the macro, and click on the pencil icon
          to display configuration options.
        </Text>
      </SectionMessage>
    </>
  );
};

// Adding the Config function to the ForgeReconciler to allow for configuration changes
ForgeReconciler.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

ForgeReconciler.addConfig(<Config />);

Install your app

If you haven't done so already, make sure you have installed the latest version of UI Kit before you begin deploying your app. Navigate to the top-level directory of the app and run npm install @forge/react@latest --save on the command line.

Build, deploy, and install the app to see it in your Confluence site.

To use your app, it must be installed onto an Atlassian site. The forge deploy command builds, compiles, and deploys your code; it'll also report any compilation errors. The forge install command then installs the deployed app onto an Atlassian site with the required API access.

You must run the forge deploy command before forge install because an installation links your deployed app to an Atlassian site.

  1. Navigate to the app's top-level directory and deploy your app by running:

    1
    2
    forge deploy
    
  2. Install your app by running:

    1
    2
    forge install
    
  3. Select your Atlassian product using the arrow keys and press the enter key.

  4. Enter the URL for your development site. For example, example.atlassian.net. View a list of your active sites at Atlassian administration.

Once the successful installation message appears, your app is installed and ready to use on the specified site. You can always delete your app from the site by running the forge uninstall command.

Running the forge install command only installs your app onto the selected product. To install onto multiple products, repeat these steps again, selecting another product each time. Note that the Atlassian Marketplace does not support cross-product apps yet.

You must run forge deploy before running forge install in any of the Forge environments.

View your app

  1. Edit a Confluence page in your development site.
  2. Select Insert icon from the toolbar from the toolbar.
  3. Find the macro by name and select it. The app is displayed on the page.
  4. Click the pencil icon to open the app's config panel on the right side of the page.
  5. Type the Pet age, Pet name, and then close the configuration page. The config values are saved automatically.

You should now see the config values displayed within the app.

Rate this page: