DEPRECATION NOTICE UI Kit 1 is now in the deprecation period. This version will no longer be supported or functional after 28 Feb 2025
To support your upgrade, please use the following guides:
We highly recommend using the latest version of UI Kit to quickly build your user interface with its updated library of components.
This page is about the previous version of UI Kit.
We highly recommend upgrading to and utilizing the latest version. Get started with UI Kit's latest library of components to quickly build your user interface.
UI Kit 1 lets you quickly and easily build a user interface for your app. It's made up of three main concepts: components, hooks, and event handlers.
Components are the visual elements that help you construct an intuitive and familiar user interface.
Hooks and event handlers enable your app to handle user interactions and return views dynamically from the server, where the UI Kit code is executed.
UI Kit 1 apps inherit modern security features to ensure high trust between Atlassian, developers, and users.
UI Kit 1 is designed for Forge apps with simpler use cases. Custom UI is a more suitable option for complex use cases. For more information on how to choose between the UI Kit and custom UI, see User interface.
This page describes the main concepts behind UI Kit 1 and how these concepts are applied in a sample Forge app. You'll need a basic knowledge of JavaScript to understand these concepts. If you've also used React before, these concepts are similar to the equivalent React concepts. However, be aware that they're not exactly the same. If you haven't used React before, then don't worry, we'll explain each concept in detail below.
A component is an element that describes part of your app’s interface. To use components, you need to understand the following concepts:
Consider the following example:
1 2import ForgeUI, { Text } from '@forge/ui'; const App = () => { return <Text>Hello world!</Text>; };
This is the code for a basic app that displays some text. In this example:
Text
is the UI Kit 1 component. UI Kit 1 components are imported from the @forge/ui
package.App
is the function component. It returns the Text
UI Kit 1 component. It also happens to be the
top-level function component that defines the whole interface.Components have a number of characteristics to consider when using them. The best way to understand them is to see them in action. Consider the following example app:
1 2import ForgeUI, { render, Fragment, Text } from '@forge/ui'; const Issue = props => { return ( <Fragment> <Text>Issue: {props.issueKey}</Text> <Text>{props.description}</Text> </Fragment> ); }; const App = () => { return ( <Fragment> <Image src="https://path/to/image.png" alt="Logo" /> <Text>Current issue:</Text> <Issue issueKey="FRG-1" description="Example description for the FRG-1 issue." /> </Fragment> ); }; export const run = render(<App/>);
This example app demonstrates the following:
Issue
function component
is returned by the App
function component.Text
and Issue
components are
wrapped in a single Fragment
component, which is returned by the App
component.Fragment
is used within the App
function by using angled brackets.src="https://path/to/image.png"
prop is passed to the Image
component to tell it what image
to show. For function components, props are passed as an object argument. You can see this above,
where the props
object is passed to the Issue
component. The {props.issueKey}
and
{props.description}
are then used to change what text is rendered.render()
function: The render function turns the top-level component into an app, by
transforming the app-as-a-functional-component into a function that is run on the Forge platform.If you are a React developer, UI Kit 1 components may look similar to React components, particularly as they are both written in JSX. However, while React apps are rendered and executed on the client side, Forge apps run on a FaaS (function as a service) platform and are executed on the server side. Therefore, if a Forge app has any interactions that use developer-written code, those interactions will run server-side and incur a reasonable delay.
Hooks make your components dynamic. They are special functions that enable you to update and use the state of your app. The state is a value that the component remembers and can be updated. Without hooks, you would not be able to access and manipulate the state of an app, as Forge apps run on a FaaS (function as a service) platform which is stateless. In addition to managing state, there are other hooks that let you access contextual data, such as the current user or Confluence page properties.
Hook functions are used inside function components and are registered to that component. The app remembers the state that is associated with that component. You can use or update this state via the hook. This also enables asynchronous callbacks in your app.
Consider the following example app. It extends the app shown in the Components section above, by adding a hook (useState
) in the App
function component:
1 2import ForgeUI, { Fragment, Text, useState } from '@forge/ui'; import api, { route } from '@forge/api'; const Issue = props => { return ( <Fragment> <Text>Issue: {props.issueKey}</Text> <Text>{props.description}</Text> </Fragment> ); }; const App = () => { const [issue, setIssue] = useState(async () => { const response = await api.asUser().requestJira(route`/rest/api/3/issue/{issueIdOrKey}`); const issueData = await response.json(); //... extract issueKey from response return issueData; }); return ( <Fragment> <Text>Current issue:</Text> <Issue issueKey={issue.key} description={issue.fields.description} /> </Fragment> ); };
useState
provides two key features:
useState
allows app developers to execute asynchronous callbacks.useState
maintains the state and allows app developers to update and access it.This example demonstrates the first feature. In the code above, the useState
hook allows the
initial state of the issue to be set asynchronously with data retrieved from the Jira REST API.
The second feature is described in the next section, as it is easier to understand managing state
when used with event handlers.
Hooks are special functions that must only be called at the top-level from within function components. For example, don’t call hooks inside conditions, loops, or nested functions.
Event handlers are functions that describe what to do when the user interacts with them. These should be familiar to you if you've used event handlers before (for example, in JavaScript). Combined with hooks, you can update state based on user interactions to build dynamic apps. Event handlers are only available for some interactive components, such as Button, where the component provides props to define the event handlers.
The following example app shows how an event handler works in Forge. It extends the app previously
shown in the Hooks section above, by adding a button that lets the user close the current issue
associated with the page. Also, the Issue
component has been updated to dynamically render based
on whether or not there is a currently open issue:
1 2import ForgeUI, { Button, Fragment, Text, useState } from '@forge/ui'; import api, { route } from '@forge/api'; const Issue = props => { if (props.issueKey) { return ( <Fragment> <Text>Issue: {props.issueKey}</Text> <Text>{props.description}</Text> </Fragment> ); } else { return <Text>There is no open issue for this page</Text>; } }; const App = () => { const issueIdOrKey = 'DOCS-1'; // key of the issue to be fetched const [issue, setIssue] = useState(async () => { const response = await api.asUser().requestJira(route`/rest/api/3/issue/${issueIdOrKey}`); const issueData = await response.json(); // code to extract issue fields from response not shown here return issueData; }); const handleCloseIssue = async () => { const issueKey = issue.key; const response = await api.asUser().requestJira(route`/rest/api/3/issue/${issueKey}`); if (response.ok()) { setIssue({key: undefined, description: undefined}); } }; return ( <Fragment> <Text>Current issue:</Text> <Issue issueKey={issue.key} description={issue.fields.description} /> <Button text="Click" onClick={handleCloseIssue} /> </Fragment> ); };
In this example, Button
component has an onClick={handleCloseIssue}
prop, where onClick
is the
event and the handleCloseIssue
function is the event handler. Passing the handleCloseIssue
function to this prop means that the function is called whenever the button is pressed.
The handleCloseIssue
function is able to access the current issue.key
(issue
returned by the
useState
hook) and makes an API request to close the issue. If successful, setIssue
is called to
update the key
and description
of the current issue to undefined
. This causes the Issue
component to render the text, There is no open issue for this page.
Note how the useState
hook makes the current state available (issue
) and provides the function
to update the state (setIssue
). This is the state management feature of this hook that was described in the previous section.
You should now understand the basics of how event handlers work, how hooks work, and how they can be used together with components. These are the basic tools you need to build an app that is dynamic and interactive. To learn more about the UI Kit, read the related pages that have been recommended throughout this page.
Rate this page: