Forge Developer

Beta

Forge Developer

Beta
Last updatedNov 10, 2020

Rate this page:

Custom UI

Custom UI provides a way to build the user interface of your app. Using custom UI, you can define your own user interface using static resources, such as HTML, CSS, JavaScript, and images. The Forge platform hosts your static resources, enabling your app to display custom UI on Atlassian products. Custom UI apps inherit modern security features to ensure high trust between Atlassian, developers, and users.

Custom UI is designed for complex use cases and increased developer flexibility. The UI kit is a more suitable option for simpler use cases. For more information on how to choose between custom UI and the UI kit, see User interface.

This page describes the main concepts behind custom UI and how these concepts are applied in a sample Forge app.

Resources

A resource is a collection of static assets, which is hosted on and distributed by Atlassian cloud infrastructure.

To use custom UI, you need to provide a resource to an eligible module with the resource property. See Modules to see which modules are eligible for custom UI.

Consider the following example manifest.yml file:

1
2
3
4
5
6
7
8
9
modules:
  jira:issuePanel:
    - key: hello-world-panel
      resource: example-resource
      title: Hello world!
      icon: https://developer.atlassian.com/platform/forge/images/issue-panel-icon.svg
resources:
  - key: example-resource
    path: static/hello-world/build

This is the manifest declaration for a basic Jira issue panel using custom UI. In this example:

  • resource is a reference to a defined key in the resources object.
  • path is the relative path from the top-level directory of your Forge app to the directory of the static assets for the resource. It should contain the index.html entry point for the custom UI app; in this case, static/hello-world/build/index.html.

Consider an example index.html file saved in the root of the resource path:

1
2
3
4
5
6
<!DOCTYPE html>
<html>
  <body>
    <div>Hello, world!</div>
  </body>
</html>

In this example, index.html contains some text that's displayed to the user when they view the issue panel. The index.html file can include any valid HTML, JavaScript, and CSS, subject to security constraints.

The index.html file can also include other files from the same resource directory using relative URLs, such as JavaScript and CSS files, and images.

For example, to include an image at static/hello-world/build/images/image.png, the index.html looks like this:

1
2
3
4
5
6
7
<!DOCTYPE html>
<html>
  <body>
    <div>Hello, world!</div>
    <img src="./images/image.png"></img>
  </body>
</html>

See Resources reference documentation for more details.

Bridge

The custom UI bridge is a JavaScript API that enables custom UI apps to securely integrate with Atlassian products.

You can install the custom UI bridge using the @forge/bridge npm package. You can import @forge/bridge using a bundler, such as Webpack.

For example, you can start by creating a new app from one of the custom UI templates. In the static/hello-world directory, running npm install && npm build will bundle the template static web application together with the custom UI bridge, into the static/hello-world/build directory, which is used as the resource path in the Forge app's manifest.yml.

In the template, the bridge is used in static/hello-world/src/App.js:

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

invoke("exampleFunctionKey", { example: "my-invoke-variable" }).then(setData);

See Custom UI bridge reference documentation for the available bridge API methods.

Security

Custom UI apps are hosted by Atlassian. This enables custom UI apps to enforce sandboxing of the static assets that are run in the user's browser. This is done by using content security policy (CSP) headers that provide protection against common security vulnerabilities, such as cross-site scripting (XSS) and data injection. For your custom UI app to work as expected, your users must be on a CSP-compatible browser.

The CSP headers used in custom UI apps prevent some behaviour, in order to prevent data egress and common security vulnerabilities where possible. For example:

  • All scripts and assets used in your custom UI app must come from the same resource directory as your custom UI app. This means that you cannot use scripts or images from external sources, such as Google Analytics or Sentry, in your static assets. This is to ensure that malicious scripts that are injected by attackers (i.e. XSS) will not be able to run.
  • In a similar way, you cannot fetch APIs from your static assets. Instead, you must use the invoke method from the custom UI bridge to run an Atlassian-hosted backend FaaS function, where you may fetch from your desired APIs, and return the required data to the frontend.

If there is code in your custom UI app that violates the CSP headers, the app will not behave as expected, and an error will be shown in the browser console.

Let us know if you have any feedback on the CSP headers we've implemented.

Inline styles

To avoid security vulnerabilities in injected inline CSS, the default custom UI CSP headers prevent inline styles from being applied. If you use inline styles, you'll see this error in your browser console: Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self'". You'll receive this error when using CSS-in-JS libraries that leverage inline styles, such as styled components.

If your custom UI app needs to use inline styles, you can modify the CSP rules by adding the following meta tag to your HTML head element:

1
2
3
4
<meta
  http-equiv="Content-Security-Policy"
  content="style-src 'unsafe-inline' 'self'"
/>

This method only works for style-src, and not for other CSP headers.

Accessing static assets

Since the static assets of a custom UI app are distributed via a URL with a particular path that identifies your app, you should use relative paths when accessing these assets from your custom UI app. For example, instead of including an image at "/assets/image.png", you should use "./assets/image.png".

If you're using create-react-app to generate your static assets (by creating a single-page React app), you can set "homepage": "." in your package.json to convert absolute paths into relative paths when bundling your app.

See the following step-by-step tutorial to start building a custom UI app in Jira.

Rate this page: