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.
UI Kit 2 offers enhanced features and capabilities compared to UI Kit. While much of the functionality remains similar, variations in the underlying architecture require you to make some modifications to adapt a UI kit app for compatibility with UI Kit 2.
Most UI Kit 2 components share the same API as their UI kit counterparts. The exceptions for some components are as follows:
The FormCondition
component is no longer used in UI Kit 2. The same conditional rendering can be achieved with onChange
handlers and state variables.
UI kit
1 2return ( <Form onSubmit={(data) => console.log(data)}> <CheckboxGroup label="More options" name="JQLOption"> <Checkbox label="Run a JQL Query" value="true" /> </CheckboxGroup> <FormCondition when="JQLOption" is={['true']}> <TextField label="JQL Query" name="query" /> </FormCondition> </Form> );
UI Kit 2
1 2const [jqlOptionChecked, setJqlOptionChecked] = useState(true); return ( <Form onSubmit={(data) => console.log(data)}> <Checkbox label="Run a JQL Query" name="JQLOption" value={jqlOptionChecked} onChange={(value) => setJqlOptionChecked(value)} /> {jqlOptionChecked && ( <TextField label="JQL Query" name="query" /> )} </Form> );
Button
, Code
, StatusLozenge
, and Tag
components no longer accept a text
property in UI Kit 2. The text content is placed in children
instead. See the following examples for the Button
and StatusLozenge
components.
UI kit
1 2<Button text="Click me!" onClick={() => console.log('Button clicked')} /> <StatusLozenge text="In Progress" appearance="inprogress" />
UI Kit 2
1 2<Button onClick={() => console.log('Button clicked')}> Click me! </Button> <StatusLozenge appearance="inprogress"> In Progress </StatusLozenge>
Not all hooks in UI Kit 1 exist in the latest version of UI Kit.
In the latest version, implementations of standard react
library hooks (useState
, useEffect
and useAction
) can now be directly accessed from react
instead (see Hooks API Reference – React). Note that the react
equivalent of UI kit's useAction
is useReducer
, although usage of both is identical.
The useProductContext
, useConfig
, useContentProperty
, useSpaceProperty
and useIssueProperty
hooks are available in our @forge/react
package.
In addition, since the context/configuration/property values outputted by the @forge/react
hooks take time to load, they will not be immediately available upon app mounting; i.e. these values will initially be undefined
before they are loaded with actual values.
UI Kit 1
1 2import ForgeUI, { useState, useEffect, useAction, Text } from '@forge/ui'; const App = () => { const [count, setCount] = useState(0); return ( <Text>Count: {count}</Text> ); }
Latest version of UI Kit
1 2import React, { useState, useEffect, useReducer } from 'react'; import { Text } from '@forge/react'; const App = () => { const [count, setCount] = useState(0); return ( <Text>Count: {count}</Text> ); };
See this guide for how to use them.
The @forge/api
package is designed to run in a Forge function (for example, UI kit app, Custom UI resolver), and so will not work in the app frontend in UI Kit 2.
To make requests to product APIs, use the requestJira
and requestConfluence
methods exported from @forge/bridge
instead.
UI kit
1 2await api.asUser() .requestJira(route`/rest/api/3/issue/${issueKey}/watchers`, { method: "POST", headers: { "Content-Type": "application/json", "Accept": "application/json" }, body: JSON.stringify(`${accountId}`) });
UI Kit 2
1 2await requestJira(`/rest/api/3/issue/${issueKey}/watchers`, { method: "POST", headers: { "Content-Type": "application/json", "Accept": "application/json" }, body: JSON.stringify(`${accountId}`) });
There is no helper method exported from @forge/bridge
to call the Storage API directly. You must instead set up a Custom UI resolver that can call the Storage API, and invoke the resolver from your app frontend.
UI kit
1 2import { storage } from '@forge/api'; const App = () => { const [storageValue, setStorageValue] = useState(undefined); return ( <> <Button text="Get value" onClick={async () => { const value = await storage.get('example-key'); setStorageValue(value); }} <Text>Value: {storageValue}</Text> </> ); };
UI Kit 2
frontend/hello-world/src/App.js
1 2import React, { useState, useEffect } from 'react'; import { Text, Button } from '@forge/react'; import { invoke } from '@forge/bridge'; const App = () => { const [storageValue, setStorageValue] = useState(0); return ( <> <Button onClick={async () => { const value = await invoke('getStorageValue', { key: `example-key` }); setStorageValue(value); }}>GetValue</Button> <Text>Value: {storageValue}</Text> </> ); };
src/index.js
1 2import Resolver from "@forge/resolver"; import { storage } from '@forge/api'; const resolver = new Resolver(); resolver.define("getStorageValue", async ({ payload, context }) => { const value = await storage.get(payload.key); return value; }); export const handler = resolver.getDefinitions();
Defining configuration options for a macro in UI Kit 2 is now different from how it is done in UI kit. It now requires the following modifications in the manifest.yml
file:
function
property with a resource
property.config
property value with a boolean: config: true
.render
, and assign its value to native: render: native
.See how to add configurations by using the Add configuration to a macro with UI Kit 2 tutorial.
UI Kit 2
1 2modules: macro: - key: pet-facts resource: main # <---- replaces function: main render: native # <---- defines this module as a UI Kit 2 module title: Pet description: Inserts facts about a pet config: true # <---- indicates that the macro extension point has a configuration panel resources: - key: main path: frontend/hello-world/src/index.js app: id: "<your app id>" name: pet-facts-macro
1 2import ForgeUI, { render, MacroConfig, TextField } from "@forge/ui"; const defaultConfig = { name: "Unnamed Pet", age: "0" }; const Config = () => { return ( <MacroConfig> <TextField name="name" label="Pet name" defaultValue={defaultConfig.name} /> <TextField name="age" label="Pet age" defaultValue={defaultConfig.age} /> </MacroConfig> ); }; export const config = render(<Config />);
A UI Kit 2 must read the app's config values asynchronously from the product context. The product context can be retrieved by calling view.getContext()
. Note that the shape of the context object returned by the view.getContext
differs slightly from the shape returned by UI kit’s useProductContext
hook. See Custom UI view for the function signature.
In addition, in UI Kit 2, to hook up the configuration component, it will need to be added into the ForgeReconciler
via the new addConfig
function. E.g: ForgeReconciler.addConfig(<Config />)
;
UI kit
1 2import ForgeUI, { useConfig, Text } from '@forge/ui'; function App() { const config = useConfig(); return ( <Text> Config: {config.age} </Text> ); }
UI Kit 2
app.jsx
1 2import React, { useEffect, useState } from 'react'; import { view } from '@forge/bridge'; import { Text } from '@forge/react'; function App() { const [productContext, setProductContext] = useState({}); useEffect(() => { view.getContext().then(setProductContext); }, []); return ( <Text> Config: {productContext?.extension?.config?.age} </Text> ); }
config.jsx
1 2import React from 'react'; import { TextField } from '@forge/react'; export const Config = () => { return ( <> {/* Form components */} <TextField name="age" label="Pet age" /> </> ); };
index.jsx
1 2import React from 'react'; import ForgeReconciler from '@forge/react'; import { Config } from '../config'; ForgeReconciler.render( <React.StrictMode> <App /> </React.StrictMode> ); ForgeReconciler.addConfig(<Config />);
Due to architectural differences, the approach for logging and debugging in UI Kit 2 is different.
As UI kit runs on the server-side, any console.log
statement in your app will be stored and available for viewing through the forge logs
CLI command.
However, in UI Kit 2, because the app runs directly on the browser, console.log
statements won't be stored and will only be available for visualization directly in your browser's developer console. If you need to log and store information, you can invoke a Resolver function and utilize console.log
statements.
Rate this page: