Bitbucket modules
Common modules
Compass modules
Confluence modules
Jira modules
Jira Service Management modules
Rovo modules (Preview)

Jira UI modifications

The jira:uiModifications module allows you to change the look and behavior of the Global issue create (GIC), Issue view and Issue transition (the new experience) when used in conjunction with the UI modifications (apps) REST API.

UI modifications (UIM) is a runtime extension which allows applications to modify the UI on supported screens in a given UIM app mounting context (for example, a combination of a project, an issue type and a view type). The UI is modified using the UIM JS API in Forge applications in the UIM Forge module. You can manage the available UI modifications and their contexts using the UIM REST API. Applications can store additional data related to UI modifications as UIM data, which is also managed by the UIM REST API. Each UI modification is backed by a UIM entity which represents it on the back-end and is delivered to the front-end in UIM data.

To understand the broader context of this module read the UI modifications guide.

UIM app

An Atlassian Forge application which uses the UIM Forge module. A single UIM app can declare only one UIM Forge module.

UIM app invocation context

Provided to the UIM app by Jira. It consists of a project, issueType, and uiModifications (UIM data).

UIM app mounting context

A combination of a project, issueType, and viewType. UIM supports the following view types:

  • Global issue create
  • Issue view
  • Issue transition

UIM data

An array of UIM entities for a given UIM app invocation context. The interpretation of UIM data is the responsibility of the UIM app. UIM data can be accessed through the invocation argument within the initCallback and the changeCallback.

UIM entity

A single mapping of custom textual data and a UIM app mounting context. It can be created and obtained using the UIM REST API.

UIM Forge bridge API

The API provided to the UIM app through the @forge/jira-bridge module. For more details, see the Jira Bridge uiModifications documentation.

UIM Forge module

A UI modifications Forge module (jira:uiModifications) declared in the manifest.

UIM REST API

The back-end REST API used to assign and retrieve specific data (related to project, issueType, and viewType) to be consumed by the UIM app through the UIM app invocation context. For more details, see the UI modifications (apps) documentation.

Writing your first UIM app

To write your first UIM app, follow the detailed instructions below or try out the example app at atlassian/forge-ui-modifications-example.

Forge manifest

You can build your Forge app following one of our guides. To create an app that will run as a UIM app, make sure to update your manifest.yml file to include:

1
2
modules:
  jira:uiModifications:
    - key: ui-modifications-app
      title: Example UI modifications app
      resource: uiModificationsApp
resources:
  - key: uiModificationsApp
    path: static/ui-modifications/dist

The application

UIM apps depend on the @forge/jira-bridge package which exposes the uiModifications API from version 0.6.0 onwards. Both initialization-triggered and user-triggered phase changes are supported. This package works on the client side and will be effective only when used within the static resource declared in the jira:uiModifications module.

The UIM Forge module will be rendered inside an invisible iframe element. The only meaningful part is the script linked in the main HTML file.

1
2
//static/hello-world/index.js
import { uiModificationsApi } from '@forge/jira-bridge';

uiModificationsApi.onInit(
  ({ api }) => {
    const { getFieldById, getScreenTabs } = api;

    // Hiding the priority field
    const priority = getFieldById('priority');
    priority?.setVisible(false);

    // Changing the summary field label
    const summary = getFieldById('summary');
    summary?.setName('Modified summary label');

    // Changing the assignee field description
    const assignee = getFieldById('assignee');
    assignee?.setDescription('Description added by UI modifications');

    // Get value of labels field
    const labels = getFieldById('labels');
    const labelsData = labels?.getValue() || [];
    labels?.setDescription(
      `${labelsData.length} label(s) are currently selected`
    );

    // Hide the last screen tab
    const tabs = getScreenTabs();
    if (tabs.length > 0) {
      tabs.at(-1).setVisible(false);
    }
  },
  () => ['priority', 'summary', 'assignee', 'labels']
);

Keep in mind that all changes requested during the run of the onInit callback will be applied at once after the function completes its execution.

Async operations

The execution of changes can be postponed, for example when a UIM needs to perform some async operations before evaluating which modification to apply. For that purpose, our API allows you to return a promise object from the callback. The changes will be postponed until the promise resolves.

A correct implementation using a promise:

1
2
import { uiModificationsApi } from '@forge/jira-bridge';

// Below: imaginary import
import { shouldPriorityBeHidden } from '../my-services';

uiModificationsApi.onInit(
  ({ api }) => {
    const { getFieldById } = api;

    // Hiding the priority field
    const priority = getFieldById('priority');
    // We store the update Promise
    const priorityUpdate = shouldPriorityBeHidden().then((result) => {
      if (result === true) {
        priority?.setVisible(false);
      }
    });

    // Changing the assignee field description
    const assignee = getFieldById('assignee');
    assignee?.setDescription('Description added by UI modifications');

    // We return the promise. In this case even the assignee field description
    // will be updated only after the priorityUpdate promise resolves.
    return priorityUpdate;
  },
  () => ['priority', 'assignee']
);

Note that async/await syntax is supported:

1
2
import { uiModificationsApi } from '@forge/jira-bridge';

// Below: imaginary import
import { shouldPriorityBeHidden } from '../my-services';

uiModificationsApi.onInit(
  async ({ api }) => {
    const { getFieldById } = api;

    // Hiding the priority field
    const priority = getFieldById('priority');
    const result = await shouldPriorityBeHidden();
    if (result === true) {
      priority?.setVisible(false);
    }

    // Changing the assignee field description
    const assignee = getFieldById('assignee');
    assignee?.setDescription('Description added by UI modifications');
  },
  () => ['priority', 'assignee']
);

Configure the UI modification

The UIM Forge module will only render when configured for a given projectId and issueTypeId using the UIM REST API. For example, there could be an AdminPage that creates a UIM using the REST API. Apps can also include custom UIM data to be passed to the UIM Forge module when it is executed, for example a list of rules that a user has selected:

1
2
import api, { route } from '@forge/api';

const createUiModification = async (projectId, issueTypeId) => {
  const result = await api.asApp().requestJira(route`/rest/api/3/uiModifications`, {
    method: "POST",
    body: JSON.stringify({
      name: 'demo-ui-modification',
      data: '["custom data", "for your app"]',
      contexts: [
        { projectId, issueTypeId, viewType: 'GIC' }, 
        { projectId, issueTypeId, viewType: 'IssueView' }
      ],
    }),
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    },
  });
  console.log(`Created UI modification with status ${result.status}`);
  return result.status;
}

This custom UIM data is available as uiModifications within the onInit and onChange callbacks. It has the following shape:

1
2
uiModifications: Array<{
  id: string,
  data?: string,
}>

Properties

PropertyTypeRequiredDescription
key

string

Yes

A key for the module, which other modules can refer to. Must be unique within the manifest.

Regex: ^[a-zA-Z0-9_-]+$

resourcestringRequired if using custom UI or the latest version of UI Kit.A reference to the static resources entry that your context menu app wants to display. See resources for more details.
titlestring or i18n objectYes

A title for the module.

The i18n object allows for translation and is available to participants of the Internationalization for Forge EAP. See i18n object.

resolver{ function: string } or
{ endpoint: string }
Yes

Set the function property if you are using a hosted function module for your resolver.

Set the endpoint property if you are using Forge remote to integrate with a remote back end.

i18n object

Internationalization (i18n) for Forge apps is now available through Forge's Early Access Program (EAP). For details on how to sign up for the EAP, see the changelog announcement.

EAPs are offered to selected users for testing and feedback purposes. APIs and features under EAP are unsupported and subject to change without notice. APIs and features under EAP are not recommended for use in production environments.

For more details, see Forge EAP, Preview, and GA.

KeyTypeRequiredDescription
i18nstringYesA key referencing a translated string in the translation files. For more details, see Translations.

Extension context

UIM Forge modules can retrieve the current project and issueType by using the useProductContext hook in UI Kit 1 or getContext bridge method in custom UI and UI Kit.

The UIM context shape on GIC is:

1
2
{
  extension: {
    type: 'jira:uiModifications',
    project: {
      id: string,
      key: string,
      type: string,
    },
    issueType: {
      id: string,
      name: string,
    },
    viewType: 'GIC'
  }
}

The UIM context shape on Issue view is:

1
2
{
  extension: {
    type: 'jira:uiModifications',
    project: {
      id: string,
      key: string,
      type: string,
    },
    issueType: {
      id: string,
      name: string,
    },
    issue: {
      id: string,
      key: string,
    }
    viewType: 'IssueView'
  }
}

The UIM context shape on Issue transition is:

1
2
{
  extension: {
    type: 'jira:uiModifications',
    project: {
      id: string,
      key: string,
      type: string,
    },
    issueType: {
      id: string,
      name: string,
    },
    issue: {
      id: string,
      key: string,
    }
    issueTransition: {
      id: string,
    }
    viewType: 'IssueTransition'
  }
}

Usage:

1
2
import { view } from '@forge/bridge';
import { uiModificationsApi } from '@forge/jira-bridge';

uiModificationsApi.onInit(async ({ api, uiModifications }) => {
  const { getFieldById } = api;
  const context = await view.getContext();

  const { project, issueType } = context.extension;
  
  uiModifications.forEach(({ data: customDataConfiguredUsingRestApi }) => {
    // ...
  });
}, ({ uiModifications }) => [
  // ...
])

Scopes

UI modifications expose customer data to the app that provides them. Therefore, you must declare either classic (recommended) or granular scopes in your manifest. Note that you always have to declare all scopes from your chosen group.

If user consent isn’t given for the scopes mentioned inside the app’s manifest, UIM won’t render (same as with any other extension point). Users will be prompted to give consent when they open GIC in a project/issue type that has been configured with a UIM. For the Issue view and Issue transition, the consent dialog is presented at the top of the view and doesn’t block the user from interacting with it.

User consent applies for all modules in the app, so consent previously given in an admin page will be valid elsewhere. Additionally, if a user does not give consent, they will be prompted again the next time the UI modification is loaded. To read more about scopes, see Permissions.

Classic scopes

1
2
permissions:
  scopes:
    - 'read:jira-user'
    - 'read:jira-work'
    - 'manage:jira-configuration'
    - 'write:jira-work'
    - 'manage:jira-project'
ScopeData exposedFieldMethod
read:jira-userUser timezone and account IDn/aview.getContext
User display name, account ID, and avatarAssigneegetValue
read:jira-workProject ID, key, and type; issue type ID and issue type name of the issue being created using the GIC form or presented on the Issue viewn/aview.getContext
Issue data of the issue being created using the GIC form or presented on the Issue viewAll supported fieldsgetValue
Field nameAll supported fieldsgetName
Field visibilityAll supported fieldsisVisible
manage:jira-configurationField descriptionAll supported fieldsgetDescription
User localen/aview.getContext
Product license statusn/aview.getContext
The following values can be modified:
  • name
  • description
  • visibility
All supported fields
  • setName
  • setDescription
  • setVisible
write:jira-workDefault field value can be modifiedAll supported fieldssetValue
manage:jira-projectScreen tabs can be modifiedAll visible screen tabs
  • setVisible
  • focus

Granular scopes

1
2
permissions:
  scopes:
    - 'read:project:jira'
    - 'read:issue-type:jira'
    - 'read:user:jira'
    - 'read:user-configuration:jira'
    - 'read:issue:jira'
    - 'read:issue-status:jira'
    - 'read:priority:jira'
    - 'read:label:jira'
    - 'read:project.component:jira'
    - 'read:project-version:jira'
    - 'read:license:jira'
    - 'read:field:jira'
    - 'read:field-configuration:jira'
    - 'read:screen-tab:jira'
    - 'write:field-configuration:jira'
    - 'write:field.default-value:jira'
    - 'write:field:jira'
    - 'write:screen-tab:jira'
ScopeData exposedFieldMethod
read:project:jiraProject ID, key, and type of the issue being created using the GIC form or presented on the Issue viewn/aview.getContext
read:issue-type:jiraIssue type ID and issue type name of the issue being created using the GIC form or presented on the Issue viewn/aview.getContext
read:user:jiraUser account IDn/aview.getContext
User account IDAssignee, Reporter, People, User picker, Multiple user pickergetValue
read:user-configuration:jiraUser timezone and localen/aview.getContext
read:issue:jiraIssue data of the issue being created using the GIC form or presented on the Issue viewAll supported fieldsgetValue
read:issue-status:jiraIssue status valueStatusgetValue
read:priority:jiraPriority valuePrioritygetValue
read:label:jiraLabel valueLabelsgetValue
read:project.component:jiraComponents valueComponentsgetValue
read:project-version:jiraVersions valueFix Versions, Affects VersionsgetValue
read:license:jiraProduct license statusn/aview.getContext
read:field:jiraField nameAll supported fieldsgetName
read:field-configuration:jiraField descriptionAll supported fieldsgetDescription
Field visibilityisVisible
read:screen-tab:jiraScreen tab identifierAll visible screen tabsgetId
Screen tab visibilityisVisible
write:field-configuration:jiraField description can be modifiedAll supported fieldssetDescription
Field visibility can be modifiedsetVisible
write:field.default-value:jiraDefault field value can be modifiedAll supported fieldssetValue
write:field:jiraField name can be modifiedAll supported fieldssetName
write:screen-tab:jiraScreen tab visibility can be modifiedAll visible screen tabssetVisible
Screen tab focus can be modifiedfocus

Additional granular scopes required for Issue view

The following scopes need to be in the app manifest only if the app will assign a UI modification to Issue view.

ScopeData exposedFieldMethod
write:issue:jiraIssue field values can be modifiedAll supported fields on Issue viewsetValue
read:issue-field-values:jiraIssue field values can be readAll supported fields on Issue viewgetValue

Additional granular scopes required for Issue transition

The following scopes need to be in the app manifest only if the app will assign a UI modification to Issue transition.

ScopeData exposedFieldMethod
read:issue.transition:jiraIssue transition field values can be readAll supported fields on Issue transitiongetValue

Required user permissions

In case a required permission isn’t assigned, the user will see the following error:

1
2
We couldn't load the UI modifications configuration for this form

Global issue create

Issue view

Issue transition

Multiple UIM apps

If you install and configure multiple UIM apps to run for a given combination of project, issue type, and view type, up to 5 apps can apply changes simultaneously. If more than 5 apps are configured, changes from the remaining apps will be disregarded. Apps apply changes asynchronously, so the order of application is random.

There may be conflicts when multiple apps attempt to modify the same field using the same FieldAPI method. In such cases, app developers will receive conflict errors via the onError handler, and users will see corresponding notifications:

If the conflict happens, the changes applied by the app which finished running last will override changes from other apps.

Global issue create

Supported project types

UIM for Global issue create (GIC) currently support the following project types:

Supported entry points

The Global issue create (GIC) modal can be opened from many places in the system. We currently support the following entry points:

  • the global Create button in the top Navigation bar
  • the c keyboard shortcut
  • the issue view Add a child issue and Create subtask buttons
  • a Forge app with custom UI using CreateIssueModal from @forge/jira-bridge

The Global issue create (GIC) modal with UIM will only open from Add a child issue and Create subtask if the summary and at least one other field is set as mandatory for the issue type.

Known limitations

Flash of unmodified fields

UI modifications are loaded after the Create issue dialog has finished loading. The user is informed about the loading state by a small spinner icon next to the label, which indicates that a UIM is running. Users can still see the fields before the modifications are applied. For example, a field will be visible for a moment before being hidden, or the default field description will be visible before it changes.

Show fields and Find your field

Global issue create > show fields

Fields can be hidden by individual users using Show fields. Data for these hidden fields is not sent to the UIM app.

Find your field does not know about fields being hidden by a UIM app using setVisible. Users may not be able to discover why a field is not visible to them.

Issue view (preview)

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.

Supported project types

UIM for Issue view currently support the following project types:

Supported entry points

The Issue view can be displayed in many places in Jira. We currently support the following entry points:

  • Full page issue view
  • Board issue view
  • Backlog issue view
  • List issue view
  • Issues issue view
  • Search issue view (global search)

Using setValue versus other methods

Because by default fields are in read mode, when your app uses the setValue method, the changes are automatically committed and persisted to the database. Calling setValue doesn’t enable edit mode. Customizations applied using other methods are only valid for the current user session.

Because many users may be viewing an issue at the same time, setValue will normally trigger an issue data refresh and show the new value to all users. However, an issue data refresh doesn’t trigger when only the user making the change is viewing the issue.

Known limitations

The consent dialog is presented at the top of the view and doesn’t block the user from interacting with it. It might not be visible without scrolling depending on your browser resolution.

Validation fails are not propagated

The issue view updates data optimistically and may process invalid input from the user before rolling back the changes. If that happens, UIM apps won’t call a second onChange and may unsync from the current state of the Issue view.

For example, if a user updates the Summary field to contain 240 characters, and then updates it again to contain 260 characters, this will trigger a validation error and the value will be rolled back to 240 characters. Your UIM app will receive both changes, but won’t receive the rollback.

Flash of unmodified fields

UI modifications are loaded after the Issue view has finished loading. For example, a field will be visible for a moment before being hidden.

Real time updates

There are no callbacks available to react to real time updates in the Issue view.

Issue transition (preview)

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.

Supported project types

UIM for Issue transition currently support the following project types:

Supported entry points

The Issue transition dialog can be opened from many places in the system. We currently support the following entry points:

  • full page Issue view:
    • changing the issue status
    • running edit status from the command palette
  • all issues page - changing issue status
  • Project > Backlog - changing the issue status
  • Plans > Timeline - changing the issue status
  • Active sprints - moving card between columns
  • Active sprints - issue dialog changing the issue status

Known limitations

Issue Transition new experience

UI modifications only run on the new experience of the Issue transition dialog. The legacy dialog isn’t and won’t be supported.

The consent dialog doesn’t block the user from interacting with the view.

Flash of unmodified fields

UI modifications are loaded after the Issue transition dialog has finished loading. The user is informed about the loading state by a small spinner icon next to the label, which indicates that a UIM is running. Users can still see the fields before the modifications are applied. For example, a field will be visible for a moment before being hidden, or the default field description will be visible before it changes.

Rate this page: