---
title: Creating an extension point - Client-side Extensions
platform: server
product: clientside-extensions
category: devguide
subcategory: extension-points
date: '2024-09-24'
---

# Creating an extension point

{{% note %}}
This guide assumes you're using the [Bitbucket CSE template](https://bitbucket.org/atlassianlabs/bitbucket-client-side-extensions-template), and followed the instructions from the [Getting started](/server/framework/clientside-extensions/guides/extension-points/) section.
{{% /note %}}

In this first section, you will learn:

-   What an extension point is
-   What schemas are and how to define them
-   How to list all the registered extensions for your extension point

You'll be working on the tutorial page: http://localhost:7990/bitbucket/plugins/servlet/extension-points

## Extension points

Extension points allow you to provide a safe place for other developers to extend your UI with their own features.

An extension point uses strongly typed contracts called schemas to specify the extensions they support and the
[attributes](/server/framework/clientside-extensions/guides/introduction/creating-an-extension/#attributes)
they can render. The validations safeguard the extension point against runtime errors.

This contract is called a `schema`, and is the central piece of CSE extension points.

## Defining a schema

You define schemas in `.graphql` files using [graphql-style syntax](https://spec.graphql.org/June2018/), which lets you create complex,
strongly typed structures to define lightweight validators that can run without affecting performance.

In `/src/main/my-app/extensions/extension-points-tutorial`, create a file called `schema.cse.graphql` and define a schema:

```graphql
"""
---
extensionPoint: extension.points.tutorial
---
"""
type Schema {

}

```

The basic structure of a CSE schema is:

-   A graphql type named `Schema`.
-   A comment annotation on top of the `Schema` type to specify the name of the extension point to be created.

### Supported attributes

In `Schema`, you can declare the attributes that determine how the extension is rendered.

For example, if you want to support a [Link](/server/framework/clientside-extensions/reference/api/extension-factories/link/) extension,
you would write a schema like the following:

```graphql
"""
---
extensionPoint: extension.points.tutorial
---
"""
type Schema {
    type: LinkExtension!

    label: String

    url: String
}
```

The CSE schema package provides a list of default
[Scalars and Types](/server/framework/clientside-extensions/reference/api/extension-points/schemas/#provided-scalars)
like `LinkExtension` and `String` to make it easier to define schemas.

In the above example, the `type` attribute declares that only extensions of type Link are supported. The attributes `label` and `url` are strings
to be used when rendering the link on the screen.

{{% warning %}}
When writing schemas, you should verify that the attributes you're requiring comply with CSE [extension factories API](/server/framework/clientside-extensions/reference/api/extension-factories/).
{{% /warning %}}

You can require as many attributes as you need, of any type you want, but there are certain attributes like `type`, `onAction`, `label` and `url`
that are expected to behave in a certain way. Make sure to check the [extension factories API](/server/framework/clientside-extensions/reference/api/extension-factories/)
reference to verify that your schema is requiring the attributes that developers expect for a certain type of extension.

### Attribute unions

In some scenarios, you might need an attribute to have more than one type. For example, you might want to support both a
link extension and button extension on the same extension point.

You can declare these attributes using a graphql union syntax as follows:

```graphql
"""
---
extensionPoint: extension.points.tutorial
---
"""
type Schema {
    type: SupportedExtensions!

    label: String

    url: String

    onAction: Function
}

union SupportedExtensions = LinkExtension | ButtonExtension
```

In the above example, you can see that an `onAction` attribute was also declared to comply with the [Button](/server/framework/clientside-extensions/reference/api/extension-factories/link/) extension API.

## Getting the extensions

Once the schema for an extension point is defined, it can be imported directly into the code using the [CSE schema-loader](/server/framework/clientside-extensions/guides/how-to/setup-schema-loader/).

{{% tip %}}
The CSE schema-loader is already installed and configured in the Bitbucket Server template for you.
{{% /tip %}}

The loader will then transform your schema into a set of hooks and components that fetches all the extensions registered
for your extension point, validate them against the required attributes, and return a list of extension descriptors.

### Extension descriptor

An extension descriptor is an object that contains the key and location of an extension, in addition to its attributes:

```ts
interface ExtensionAttribute {
    key: string;
    location: string;
    attributes: {
        // the attributes of an extension
    };
}
```

### Importing the helpers

To get the helpers that fetch and validate the extensions, import them from the schema as follows.

On `./src/main/my-app/extensions/extension-points-tutorial/extension-points-page.jsx`, write:

```jsx
import React from 'react';

import { useExtensions } from './schema.cse.graphql';

const MyPage = () => {
    // get all supported extensions registered for extension.points.tutorial
    const extensions = useExtensions();

    console.log(extensions);

    return (
        <PageContainer>
            <h2>extension.points.tutorial</h2>
        </PageContainer>
    );
};

/** page declaration for guides only **/
```

Go to http://localhost:7990/bitbucket/plugins/servlet/extension-points and open your console.

You should see a list of extension descriptors like:

```js
[
    {
        key: "com.atlassian.myapp.bitbucket-plugin-template:extensions__extension-points-tutorial__extensions__button-extension__js",
        location: "extension.points.tutorial",
        attributes: {
            label: "Tutorial: button extension",
            onAction: ƒ (),
            type: "button"
        }
    },
    {
        key: "com.atlassian.myapp.bitbucket-plugin-template:extensions__extension-points-tutorial__extensions__link-extension__js"
        location: "extension.points.tutorial",
        attributes: {
            label: "Tutorial: link extension"
            type: "link"
            url: "http://go.atlassian.com/clientside-extensions"
        }
    }
```

These extensions are provided by the template in order to make it easier to follow these guides, and are located under
`/src/main/my-app/extensions/extension-points-tutorial/extensions` folder.

### Showing a loading indicator

The CSE schema-loader provides other helpers and components. For example, it provides a hook to tell if extensions
are being loaded.

On `./src/main/my-app/extensions/extension-points-tutorial/extension-points-page.jsx`, write:

```jsx
import React from 'react';

import { useExtensions, useExtensionsLoadingState } from './schema.cse.graphql';

const MyPage = () => {
    const extensions = useExtensions();
    const loading = useExtensionsLoadingState();

    return (
        <PageContainer>
            <h2>extension.points.tutorial</h2>
            {loading && (
                <p>
                    <b>loading...</b>
                </p>
            )}
        </PageContainer>
    );
};

/** page declaration for guides only **/
```

{{% tip %}}
For a list of all the helpers and components provided by the schema-loader, refer to
[Hooks and components](/server/framework/clientside-extensions/reference/api/extension-points/hooks-and-components) guide.
{{% /tip %}}

## Recap and next steps

So far, you've learned:

-   Extension points allow other developers to enhance your UI with more features.
-   Each extension point is defined as a schema, which is a contract between your extension point and developers.
-   How to get the extensions registered for your extension point, and how to show a loading indicator while they load.

Next, you're going to learn how to [render your extensions](/server/framework/clientside-extensions/guides/extension-points/rendering-extensions).
