---
title: Extension API - Client-side Extensions
platform: server
product: clientside-extensions
category: reference
subcategory: api
date: '2024-09-24'
---

# Extension API

## About

Top level API to manipulate re-render calls and cleanup for extensions.

## updateAttributes()

Updates the specified attributes with a new set of values.

### Signature

```ts
type AttributeUpdatePayload = ExtensionAttributes | (previousAttributes: ExtensionAttributes) => ExtensionAttributes;

interface ExtensionAPI {
    updateAttributes: (payload: AttributeUpdatePayload) => void;
}
```

### Arguments

<table>
    <thead>
        <tr class="header">
            <th>Name</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>payload*</td>
            <td><code>AttributeUpdatePayload</code></td>
            <td>
                <p>
                    <strong>Setting an object</strong><br/>
                    Setting an object with the attributes that want to be changed and their new values.
                </p>
                <p>
                    <strong>Using a callback</strong><br/>
                    A callback function that will receive the previous value of attributes as a parameter, and should return an object
                    with new attributes.
                </p>
            </td>
        </tr>
    </tbody>
</table>

<p><strong>* required</strong></p>

### Usage

#### Setting an object

```ts
import { ButtonExtension } from '@atlassian/clientside-extensions';

import { someOperation } from './some-operation';

/**
 * @clientside-extension
 * @extension-point reff.plugins-example-location
 */
export default ButtonExtension.factory((extensionAPI, context) => {
    return {
        label: 'My Button',
        onAction: () => {
            extensionAPI.updateAttributes({ isLoading: true });

            someOperation().then((res) => {
                if (!res) {
                    return extensionAPI.updateAttributes({ isLoading: false, isDisabled: true });
                }

                return extensionAPI.updateAttributes({ isLoading: false });
            });
        },
    };
});
```

#### Using a callback

The new value of attributes can be computed based on the previous value of attributes. To do that, you can pass a callback function to the `updateAttributes`.
The function will receive the previous set of attributes, and it needs to return a new set of attributes.

```ts
import { ButtonExtension } from '@atlassian/clientside-extensions';

import { initialize } from './some-operation';

/**
 * @clientside-extension
 * @extension-point reff.plugins-example-location
 */
export default ButtonExtension.factory((extensionAPI, context) => {
    initialize(context).then((res) => {
        extensionAPI.updateAttributes((prevAttributes) => ({
            isLoading: false,
            isDisabled: prevAttributes.isDisabled || res.active,
        }));
    });

    return {
        label: 'My Button',
        isDisabled: !context || !context.id,
        isLoading: true,
        onAction: () => {
            // ... some action
        },
    };
});
```

{{% note %}}
The attributes returned by the callback will get automatically merged with the previous attributes.
{{% /note %}}

#### Example

```ts
export default ButtonExtension.factory((extensionAPI, context) => {
    fetchRequest().then((response) => {
        extensionAPI.updateAttributes(() => ({
            isDisabled: !response.active,
            isLoading: false,
        }));

        // After calling the `updateAttributes` with a callback the new attributes
        // set will look like this:
        //
        // {
        //   label: 'My Button',
        //   isDisabled: true || false,
        //   isLoading: false,
        //   onAction: () => { ... },
        // }
    });

    return {
        label: 'My Button',
        isDisabled: !context || !context.id,
        isLoading: true,
        onAction: () => {
            // ... some action
        },
    };
});
```

## onCleanup()

Allows to execute a clean up callback when the extension is about to be removed from the screen.

### Signature

```ts
type ExtensionCleanupCallback = () => void;

interface ExtensionAPI {
    onCleanup: (callback: ExtensionCleanupCallback) => void;
}
```

### Arguments

<table>
    <thead>
        <tr class="header">
            <th>Name</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>callback*</td>
            <td><code>ExtensionCleanupCallback</code></td>
            <td>
                Callback to be executed before removing the extension from the screen.
            </td>
        </tr>
    </tbody>
</table>

<p><strong>* required</strong></p>

### Usage

```ts
import { ButtonExtension } from '@atlassian/clientside-extensions';

import { likeService } from './like-service';

/**
 * @clientside-extension
 * @extension-point reff.plugins-example-location
 */
export default ButtonExtension.factory((extensionAPI, context) => {
    const likeSubscription = likeService.getLikes(context).subscribe((res) => {
        extensionAPI.updateAttributes({
            isLoading: false,
            label: res.liked ? `Liked by you and ${res.likes} others.` : 'Like this button',
        });
    });

    const likeHandler = () => {
        // handle like...
    };

    extensionAPI.onCleanup(() => {
        likeSubscription.unsubscribe();
    });

    return {
        label: 'Like this button',
        isLoading: true,
        onAction: likeHandler,
    };
});
```
