Last updated Dec 20, 2023

Create a GIPHY app using the UI kit on Confluence

This tutorial describes how to make API calls to an external API from your Forge app and display the result on a Confluence Cloud page. You’ll use the GIPHY API to display GIFs on the Confluence page. The result will look like this:

A gif is added to a Confluence page

Before you begin

This tutorial assumes you're already familiar with the basics of Forge development. If this is your first time using Forge, see Getting started first.

To complete this tutorial, you need the following:

During your GIPHY API key generation, you will be prompted to select the type of API. Make sure to select API (not SDK).

Set up a cloud developer site

An Atlassian cloud developer site lets you install and test your app on Confluence and Jira products set up for you. If you don't have one yet, set it up now:

  1. Go to http://go.atlassian.com/cloud-dev and create a site using the email address associated with your Atlassian account.
  2. Once your site is ready, log in and complete the setup wizard.

You can install your app to multiple Atlassian sites. However, app data won't be shared between separate Atlassian sites, products, or Forge environments.

The limits on the numbers of users you can create are as follows:

  • Confluence: 5 users
  • Jira Service Management: 1 agent
  • Jira Software and Jira Work Management: 5 users

The Atlassian Marketplace doesn't currently support cross-product apps. If your app supports multiple products, you can publish two separate listings on the Marketplace, but your app won't be able to make API calls across different products and instances/installations.

Before we dive into the code, let's review how this app works.

A flow diagram describing how the app interacts with the GIPHY API and Confluence site

The flow diagram shows the app’s 3 main parts.

  • Macro module: Defines the name of the macro in Confluence and associates it with a Forge function that should run when you use the macro on a page in your Confluence Cloud site.
  • Function module: The function you’ll write that defines the UI component to be displayed in the app.
  • GIPHY API: The API used to fetch the GIFs from GIPHY.

These parts work together as follows:

  1. When you open a Confluence page that includes this macro, it runs the function you’ve associated with the macro in your app’s manifest.
  2. That function fetches GIFs using the GIPHY API.
  3. The GIPHY app adds the fetched GIF to the UI, which is rendered by the app.

Step 1: Create your app

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. For example, giphy-app.

  4. Select the UI Kit category from the list.

  5. Select the Confluence product from the list.

  6. Select the confluence-macro-typescript template from the list.

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

    1
    2
    cd giphy-app
    

Step 2: Configure the app manifest

This app uses a Confluence macro module. Macros enable you to add functionality or include dynamic content in a Confluence page.

  1. In the app’s top-level directory, open the manifest.yml file.
  2. Change the key under macro to giphy.
  3. Change the title under macro to GIPHY.
  4. Change the description under macro to View random GIPHY gifs!
  5. Add image egress permissions to whitelist a GIPHY image.

We will be adding this GIPHY source link: https://media3.giphy.com/media/26vUJR5VABcUJaCTm/200.gif?cid=74f3ab6481fcd606c80e02418b301c17130050edc03b7521&rid=200.gif

1
2
    permissions:
        external:
            images:
              - '<GIPHY source link >'

Your manifest file should look like this:

1
2
permissions:
  external:
    images:
      - 'https://media3.giphy.com/media/26vUJR5VABcUJaCTm/200.gif?cid=74f3ab6481fcd606c80e02418b301c17130050edc03b7521&rid=200.gif'
modules:
  macro:
    - key: giphy
      function: main
      title: GIPHY
      description: View random GIPHY gifs!
  function:
    - key: main
      handler: index.run
app:
  id: '<your-app-id>'

See Manifest to learn more about the manifest file.

Step 3: Add a user interface

Install the latest versions of the following packages in the top-level directory of the app:

  • UI kit:
    • To update your version run npm install @forge/ui@latest --save on the command line.
  • Forge API:
    • To update your version run npm install @forge/api@latest on the command line.

Add UI kit components that render when the app is called. You’ll use a sample response from the GIPHY API to make a static app (steps 1 and 3 from the flow diagram above).

  1. Open the src/index.tsx file.

  2. Replace the contents of the file with:

    1
    2
    // Import required components from the UI kit
    import ForgeUI, { render, Text, Fragment, Image } from '@forge/ui';
    
    // ImageCardProps interface which will be used by ImageCard component
    interface ImageCardProps {
        title: string;
        src: string;
    }
    
    // ImageCard component containing text and image
    const ImageCard = ({title, src}: ImageCardProps) => (
        <Fragment>
            <Text>{title}</Text>
            <Image src={src} alt={title}/>
        </Fragment>
    );
    
    // App function will return the final output
    const App = () => {
        const {title, url} = {
            title: "awesome avalanche GIF",
            url: "https://media3.giphy.com/media/26vUJR5VABcUJaCTm/200.gif?cid=74f3ab6481fcd606c80e02418b301c17130050edc03b7521&rid=200.gif"
        };
    
        return (
            <Fragment>
                <Text>Random GIF!</Text>
                <ImageCard src={url} title={title}/>
            </Fragment>
        );
    };
    
    // Exporting the above App function by exporting via 'run'
    export const run = render(<App/>);
    

In this code:

Step 4: Install your app

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.

Step 5: View your app

With your app installed, it’s time to see the app on a page.

  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.
  4. Publish the page.

The app is inserted into a Confluence page

Step 6: Call the GIPHY API to retrieve random images

Next, we'll turn the static app into a dynamic app by replacing the hardcoded response with an API call (step 2 from the flow diagram above).

To allow our app to access external resources:

  • Update the manifest to allow API calls to the GIPHY API and add the GIPHY API domain (api.giphy.com) to the list of approved domains under permissions.external.fetch.backend.
  • Whitelist the domain to access the GIPHY imagess under permissions.external.images

Your manifest.yml permissions should look like this:

1
2
  external:
    fetch:
      backend:
        - 'api.giphy.com'
    images:
      - 'media3.giphy.com'

See fetch and images permissions for information.

You’ll speed up your development process by starting a tunnel, which automatically deploys your local code changes to the cloud and makes them available to your Confluence site. You need to set the GIPHY API key as a local variable so your code can access it when making calls to the GIPHY API.

Start the Forge tunnel and modify the app’s code as follows:

  1. Copy the GIPHY API key that you have created, and start the tunnel by running:

    1
    2
    FORGE_USER_VAR_GIPHY_API_KEY=<paste-your-giphy-api-key> forge tunnel
    

    To use forge tunnel, you must have a ngrok account. See Providing credentials for ngrok for more details.

  2. Open the src/index.tsx file.

  3. Add the following code directly after the import statement to call the GIPHY API:

    1
    2
    import api from "@forge/api";
    
    // GIPHY API base URL
    const GIPHY_API_BASE = 'https://api.giphy.com/v1/gifs/';
    
    // GiphyJson interface to be used by our getRandomGif function
    interface GiphyJson {
      title: string;
      url: string;
    }
    
    // getRandomGif function makes the GIPHY API call to get a random GIF and filter out title and url
    const getRandomGif = async (): Promise<GiphyJson> => {
      console.log("Making GIPHY API call...")
      const response = await api.fetch(
        `${GIPHY_API_BASE}random?api_key=${process.env.GIPHY_API_KEY}&rating=g`,
      );
    
      const {
        data: {
          title,
          images: {
            fixed_height: { url },
          },
        },
      } = await response.json();
    
      return {
        title,
        url,
      };
    };
    

    In this code:

    1. GIPHY_API_BASE is a constant containing the URL to call the GIPHY API.
    2. The GiphyJson interface has title and url properties for the ImageCard.
    3. getRandomGif is an asynchronous function that makes the API call and returns the result in the format of GiphyJson.
    4. fetch from the Runtime API makes a call to GIPHY’s random endpoint and stores the response. This function uses the GIPHY API key from your environment variables with process.env.GIPHY_API_KEY.
  4. Add the UI kit hook useAction to the import statement from @forge/ui.

  5. Replace const { title, url } and its value in the App function with the following to update the state:

    1
    2
    const [{ title, url }, setRandomGif] = useAction(getRandomGif, getRandomGif);
    

The first parameter of useAction defines how to update the state, and the second parameter is the initial value. useAction returns the current value, and a function you can call to update the value.

Your index.tsx file should look like this:

1
2
// Importing required components from the UI kit
import ForgeUI, { render, Text, Fragment, Image, useAction } from '@forge/ui';
// Importing the api object
import api from "@forge/api";

// GIPHY API base URL
const GIPHY_API_BASE = 'https://api.giphy.com/v1/gifs/';

// GiphyJson interface to be used by our getRandomGif function
interface GiphyJson {
    title: string;
    url: string;
}

// getRandomGif function makes the GIPHY API call to get a random GIF and filter out title and url
const getRandomGif = async (): Promise<GiphyJson> => {
    console.log("Making GIPHY API call...")
    const response = await api.fetch(
        `${GIPHY_API_BASE}random?api_key=${process.env.GIPHY_API_KEY}&rating=g`,
    );

    const {
        data: {
            title,
            images: {
                fixed_height: { url },
            },
        },
    } = await response.json();

    return {
        title,
        url,
    };
};

// ImageCardProps interface which will be used by ImageCard component
interface ImageCardProps {
    title: string;
    src: string;
}

// ImageCard component containing text and image
const ImageCard = ({title, src}: ImageCardProps) => (
    <Fragment>
        <Text>{title}</Text>
        <Image src={src} alt={title}/>
    </Fragment>
);

// App function will return the final output
const App = () => {
    const [{ title, url }, setRandomGif] = useAction(getRandomGif, getRandomGif);

    return (
        <Fragment>
            <Text>Random GIF!</Text>
            <ImageCard src={url} title={title}/>
        </Fragment>
    );
};

// Exporting the above App function by exporting via 'run'
export const run = render(<App/>);

When you refresh the page in Confluence, a random GIF is displayed.

Step 7: Add a button

Instead of refreshing the page, add a button to load a new GIF. This will use the Button component from the UI kit.

  1. Open the src/index.tsx file.

  2. Add Button to the UI kit import statement.

  3. Add the button by replacing the return statement in the App function with:

    1
    2
    return (
        <Fragment>
          <Text>Random GIF!</Text>
          <ImageCard src={url} title={title} />
          <Button
            text="🔀 Shuffle!"
            onClick={() => {
              setRandomGif();
            }}
          />
        </Fragment>
    );
    

Your index.tsx file should look like this:

1
2
// Importing required components from the UI kit
import ForgeUI, { render, Text, Fragment, Image, useAction, Button } from '@forge/ui';
// Importing the api object
import api from "@forge/api";

// GIPHY API base URL
const GIPHY_API_BASE = 'https://api.giphy.com/v1/gifs/';

// GiphyJson interface to be used by our getRandomGif function
interface GiphyJson {
    title: string;
    url: string;
}

// getRandomGif function makes the GIPHY API call to get a random GIF and filter out title and url
const getRandomGif = async (): Promise<GiphyJson> => {
    console.log("Making GIPHY API call...")
    const response = await api.fetch(
        `${GIPHY_API_BASE}random?api_key=${process.env.GIPHY_API_KEY}&rating=g`,
    );

    const {
        data: {
            title,
            images: {
                fixed_height: { url },
            },
        },
    } = await response.json();

    return {
        title,
        url,
    };
};

// ImageCardProps interface which will be used by ImageCard component
interface ImageCardProps {
    title: string;
    src: string;
}

// ImageCard component containing text and image
const ImageCard = ({title, src}: ImageCardProps) => (
    <Fragment>
        <Text>{title}</Text>
        <Image src={src} alt={title}/>
    </Fragment>
);

// App function will return the final output
const App = () => {
    const [{ title, url }, setRandomGif] = useAction(getRandomGif, getRandomGif);

    return (
        <Fragment>
            <Text>Random GIF!</Text>
            <ImageCard src={url} title={title} />
            <Button
                text="🔀 Shuffle!"
                onClick={() => {
                    setRandomGif();
                }}
            />
        </Fragment>
    );
};

// Exporting the above App function by exporting via 'run'
export const run = render(<App/>);

Now you can use the button to display a new GIF.

Step 8: Deploy your updates

Now that the code is working, set your app environment variable and deploy the app so it keeps working after you close the tunnel.

  1. Store the GIPHY API key in your app environment variables by running:

    1
    2
    forge variables set GIPHY_API_KEY <your-giphy-api-key>
    
  2. Deploy the app by running:

    1
    2
    forge deploy
    

That’s it. You now have an app that fetches data from an external API and renders the result in the Confluence editor.

Next steps

Continue to one of the other tutorials or look through the reference pages to learn more.

Rate this page: