---
title: Extension Points v1 (deprecated) - Client-side Extensions
platform: server
product: clientside-extensions
category: reference
subcategory: api
date: '2024-09-24'
---

# Extension Points v1 (deprecated)

{{% warning %}}
This is a guide for the deprecated CSE version v1.x.

Visit the [updated reference](/server/framework/clientside-extensions/reference/api/extension-points/)
guide for the latest API.
{{% /warning %}}

This is a guide for product developers or extension point owners.

First, make sure to install and provide Client-side Extensions in your Product as described in the [Installation Guide for v1](https://bitbucket.org/atlassian/atlassian-clientside-extensions/src/v1.2.9/INSTALLATION.md).

Next, add `@atlassian/clientside-extensions-components` to your project.
These are a set of React components and hooks that will help you use the APIs when your code is written in React. As of this point no other framework is supported.

```
yarn add -D @atlassian/clientside-extensions-components

# or

npm install --save-dev @atlassian/clientside-extensions-components
```

## Hooks and components

### `useExtensions`

In order to provide a new extension point, import the `useExtensions` hook from the `@atlassian/clientside-extensions-components` package and use it as follows:

```jsx
import React from 'react';
import { useExtensions } from '@atlassian/clientside-extensions-components';

// 1. Define a JSON schema that describes the extension attributes.
// To read more about JSON schema format check the official docs https://json-schema.org/
const extensionPointSchema = {
    type: 'object',
    properties: {
        type: {
            enum: ['button'],
            description: 'This extension point supports a button type',
        },
        label: {
            type: 'string',
            description: 'This button label',
        },
        onAction: {
            typeof: 'function',
            description: 'Callback that will be run when user clicks the button',
        },
    },
};

export default function MyComponent() {
    // 2. Use the `useExtensions` hook to retreive the extensions. The first param passed to the hook is the name of the extension point.
    // This is what the extension developers will use to create extensions so think carefully how to name it.
    // The second is the context value. We will talk about it later.
    // Finally, the third param is the options object where you provide the JSON schema for the extension attributes.
    const extensions = useExtensions('example.extension.point.name', null, { schema: extensionPointSchema });

    return (
        </>
            {extensions.map(extensionDescriptor => {
                // 3. Read the attributes from the descriptor and render the button
                const { key, attributes } = extensionDescriptor;

                return <button key={key} onClick={attributes.onAction}>{attributes.label}</button>
                // ...render them as you need
            })}
        </>
    );
}
```

What you receive is a list of descriptors that then you can render as you want. The shape of such descriptors is:

```ts
interface ExtensionDescriptor {
    // Unique extension key
    key: string;

    // Location, the name of the extension point
    location: string;

    // A number that can be used to decide about the order of extensions
    weight: number;

    // A map of all the attributes provided by the extension author
    attributes: {
        type: string;
        onAction?: () => void;
        [key: string]: unknown;
    };
}
```

You probably are most interested in the `attributes` property, which will be populated with the attributes provided by the extensions.

Descriptors are delivered already sorted by `weight` (from highest to lowest), but you can always sort them differently before rendering.

### `useExtensionsLoadingState`

You can use this hook in order to indicate your users that extensions, and their attributes are loading.

```jsx
import React from 'react';
import { useExtensions, useExtensionsLoadingState } from '@atlassian/clientside-extensions-components';
import schema from './schema.json';

export default function MyComponent() {
    const extensions = useExtensions('example.extension.point.name', null, { schema });
    const loading = useExtensionsLoadingState('example.extension.point.name', null, { schema });

    return (
        <>
            {loading ? 'loading...' : extensions.map(ext => (
                // ...render them as you need
            ))}
        </>
    )
}
```

### `useExtensionsUnsupported`

There might be cases where you want to support legacy `WebItems` that are created using only the XML definitions inside `atlassian-plugin.xml` plugin descriptor file.

You can make use of `useExtensionsUnsupported` hook to gather all those extensions, and then show them as you want.

```jsx
import React from 'react';
import { useExtensionsUnsupported } from '@atlassian/clientside-extensions-components';
import schema from './schema.json';

export default function MyComponent() {
    const extensions = useExtensionsUnsupported('example.legacy-web-item-location-name', null, { schema });

    return (
        <>
            {extensions.map((ext) => (
                <a href="ext.url">{ext.label}</a>
            ))}
        </>
    );
}
```

### `useExtensionsAll`

You can use `useExtensionsAll` in case you need all the results from `useExtensions`, `useExtensionsLoadingState` and `useExtensionsLoadingState` and prefer to get the results with a single hook.

```jsx
import React from 'react';
import { useExtensionsAll } from '@atlassian/clientside-extensions-components';
import schema from './schema.json';

export default function MyComponent() {
    const [extensions, legacyExtensions, loading] = useExtensionsAll('example.extension.point.name', null, { schema });

    const allExtensions = [...extensions, ...legacyExtensions].sort((a, b) => a.weight - b.weight);

    return (
        <>
            {loading ? 'loading...' : allExtensions.map(ext => (
                // ...render them as you need
            ))}
        </>
    );
}
```

## Context

You can share some context data with the extensions. Think about the context as the payload of the extension. This is especially useful
when you want to provide extension authors with additional data that developers can use to build the extension.

```jsx
import React from 'react';
import { useExtensions } from '@atlassian/clientside-extensions-components';
import extensionSchema from './schema.json'; // A JSON schema that describes the extension attributes

// A JSON schema that describes the extension context
const extensionContextSchema = {
    type: 'object',
    properties: {
        issueKey: {
            type: string,
            description: 'A Jira issue key',
        }
    }
};

export default function IssueView({ issueKey }) {
    const context = {
        issueKey: issueKey,
    };
    const [extensions, isLoading] = useExtensions('example.extension.point.name', context, {
        schema: extensionSchema,
        contextSchema: extensionContextSchema,
    });

    return (
        <>
            {extensions.map(descriptor => (
                // ...render them as you need
            ))}
        </>
    )
}
```

## Schemas and `ExtensionPointInfo` component

In order to inform extension developers which types of extensions and set of attributes are supported in your locations, you need to define a Schema object.

A Schema is [JSON Schema](https://json-schema.org/) that might looks as this example:

`schema.json`

```json
{
    "type": "object",
    "properties": {
        "type": {
            "type": "string",
            "description": "Supported extension types",
            "enum": ["modal", "link", "button", "panel"]
        },
        "onAction": {
            "description": "Callback triggered on user interaction with the extension. Signature depends on extension type.",
            "type": "function"
        },
        "glyph": {
            "type": "string",
            "description": "Atlaskit Glyph name to render as an icon",
            "enum": ["cross", "check"]
        },
        "tooltip": {
            "type": "string",
            "description": "Tooltip content"
        },
        "hidden": {
            "type": "boolean",
            "description": "Hidden flag to hide the extension"
        },
        "disabled": {
            "type": "boolean",
            "description": "Disabled flag to disable the extension"
        },
        "loading": {
            "type": "boolean",
            "description": "Renders the extension in a loading state (if supported)"
        }
    },

    "required": ["type", "glyph", "tooltip"]
}
```

You can describe as many attributes as you need, but don't forget to specify the types you support in your extension point
in order to avoid unhandled extensions.

Don't set `onAction` as required if you're accepting `Link` extensions, since they don't use the `onAction` API.

### `ExtensionPointInfo`

You can add this component to share with extension developers the schema of your Locations, and also highlight the locations in
the current screen.

The information will only be available if the product has enabled their display in development mode.

```jsx
import React from 'react';
import { useExtensions, ExtensionPointInfo } from '@atlassian/clientside-extensions-components';

import schema from './schema.json';
import contextSchema from './context-schema.json';

export default function MyComponent({ context }) {
    // Third param of `useExtensions` is a configuration object.
    // Add the schema as a property of it.
    const [extensions, isLoading] = useExtensions('example.extension.point.name', context, { schema, contextSchema });

    return (
        <>
            <h4>
                Product Location
                <ExtensionPointInfo name="example.extension.point.name" schemas={{ schema, contextSchema }} />
            </h4>
            {extensions.map(ext => (
                // ...render them as you need
            ))}
        </>
    )
}
```

Render the `ExtensionPointInfo` in a place that's visible even in edge cases, like when the location is inside a
dropdown (place it in the dropdown trigger), or part of a Grid (place it in the header).

## Default extension types and handlers

Handlers continue to work the same on the recent version of CSE. Refer to the [extension handlers](/server/framework/clientside-extensions/reference/api/extension-points/extension-handlers/) guide to learn how to use them.

{{% warning %}}
Refer to the [extension handlers](/server/framework/clientside-extensions/reference/api/extension-points/extension-handlers/) guide.
{{% /warning %}}
