---
title: Page - Client-side Extensions
platform: server
product: clientside-extensions
category: reference
subcategory: api
date: '2021-10-12'
---

# Page

A page extension allows the creation of custom web page and rendering a navigation link in the product UI.

## Supported attributes

<table>
    <thead>
        <tr class="header">
            <th>Name</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td colspan="3">This extension doesn't support any attributes</td>
        </tr>
    </tbody>
</table>

## Supported annotations

<table>
    <colgroup>
        <col width="30%" />
        <col width="10%" />
        <col width="60%" />
    </colgroup>
    <thead>
        <tr class="header">
            <th>Name</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>@clientside-extension</code> <strong>*</strong></td>
            <td><code>-</code></td>
            <td>Indicates that the next function is an extension factory to be consumed by the webpack plugin.</td>
        </tr>
        <tr>
            <td><code>@extension-point</code> <strong>*</strong></td>
            <td><code>string</code></td>
            <td>Defines the location where the page navigation link will be rendered.</td>
        </tr>
        <tr>
            <td><code>@label</code> <strong>*</strong></td>
            <td><code>string</code></td>
            <td>Defines a label that's going to be used when rendering navigation link. The value can be either a raw string or a property key than will be translated.</td>
        </tr>
        <tr>
            <td><code>@page-url</code> <strong>*</strong></td>
            <td><code>string</code></td>
            <td>
                <p>Defines an url of the web page. The page will be accessible at this url prefixed with <code>/plugins/servlet/&lt;&lt;page-url&gt;&gt;</code></p>
                <p><strong>Example</strong>: Using <code>@page-url /labels</code> will register the page at <code>/plugins/servlet/labels</code> URL.</p>
            </td>
        </tr>
        <tr>
            <td><code>@page-title</code> <strong>*</strong></td>
            <td><code>string</code></td>
            <td>Defines a title of the web page. The value can be either a raw string or a property key than will be translated.</td>
        </tr>
        <tr>
            <td><code>@page-decorator</code></td>
            <td><code>string</code></td>
            <td>
                <p>A custom page decorator. The default value is <code>atl.general</code>. To read more about page decorators <a href="/server/framework/atlassian-sdk/using-standard-page-decorators">refer to the documentation</a>.</p>
                <p>List of built-in decorators supported by all products:</p>
                <ul>
                    <li><code>atl.admin</code></li>
                    <li><code>atl.general</code></li>
                    <li><code>atl.popup</code></li>
                    <li><code>atl.userprofile</code></li>
                </ul>
            </td>
        </tr>
        <tr>
            <td><code>@page-data-provider</code></td>
            <td><code>string</code></td>
            <td>
                <p>A page data provider Java class. Page data providers can be used to provide a custom set of a server-side data into your browser page extension.</p>
                <p>To read more about the page data providers check the usage section.</p>
                <p><strong>Example</strong>: <code>@page-data-provider com.example.plugin.MyDataProvider</code></p>
            </td>
        </tr>
        <tr>
            <td><code>@link</code></td>
            <td><code>string</code></td>
            <td>
                <p>Defines a custom URL that's going to be used when rendering navigation link. By default, the link value is not required.</p>
                <p>Setting a custom link will overwrite the one generated from the <code>@page-url</code> annotation value.</p>
            </td>
        </tr>
        <tr>
            <td><code>@weight</code></td>
            <td><code>number</code></td>
            <td>
                <p>Determines the order in which the navigation link appears respect to others in the same location.</p>
                <p>Extensions are displayed top to bottom or left to right in order of ascending weight.</p>
            </td>
        </tr>
        <tr>
            <td><code>@condition</code></td>
            <td><code>string | Conditions</code></td>
            <td>
                <p>Defines one or multiple conditions that must be satisfied for the navigation link to be displayed.</p>
                <p>The conditions are evaluated on the server, and created with Java.</p>
                <p>If one of the conditions is not met, the code of the extension won't be loaded in the client.</p>
                <p>For more information about the conditions please refer to the <a href="/server/framework/atlassian-sdk/web-section-plugin-module/#condition-and-conditions-elements">examples of Web items documentation</a>.</p>
            </td>
        </tr>
    </tbody>
</table>

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

## Usage

### Basic page

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

/**
 * @clientside-extension
 * @extension-point example.extension.point
 * @label "My page"
 * @page-url /my-page
 * @page-title "My page"
 */
export default PageExtension.factory((container) => {
    // attach content to the container

    container.innerHTML = 'My new page!';
});
```

### Using translations

The `@label` and `@page-title` annotations support passing either a raw string value or a property key that will be used to render translated value.

For more information about the translation mechanism and the `*.properties` files, [refer to the documentation](/server/framework/atlassian-sdk/internationalising-your-plugin/).

#### `my-translations.properties`

```properties
some.property.key.button.title = A custom page
some.property.key.page.title = My custom page
```

#### `my-extension.ts`

```ts
/**
 * @clientside-extension
 * @extension-point example.extension.point
 * @label some.property.key.button.title
 * @page-title some.property.key.page.title
 * @page-url /my-custom-page
 */
export default PageExtension.factory((container) => {
    container.innerHTML = '<div>My custom page</div>';
});
```

### Using a custom page decorator

The `@page-decorator` annotation allows you to configure the base markup of the page. By default, the page will be rendered using the `atl.general` decorator that is a standard blank page with basic navigation.

If your page needs to be rendered with the admin layout, you can use `atl.admin` decorator. Different products can provide an additional custom page decorators you can use.

To read more about page decorators, [refer to the documentation](/server/framework/atlassian-sdk/using-standard-page-decorators).

```ts
/**
 * @clientside-extension
 * @extension-point example.extension.point
 * @label "My admin page"
 * @page-url /my-admin-page
 * @page-title "My admin page"
 * @page-decorator atl.admin
 */
export default PageExtension.factory((container) => {
    container.innerHTML = '<div>Very secure page!</div>';
});
```

### Using data provider

If you need to retrieve a server-side data in your page extension, you can use the `@page-data-provider` annotation to specify a Java data provider class.

The extension data provider class needs to implement `ExtensionDataProvider` interface and return `Jsonable` data.

For information about returning the more complex large JSON structures refer to the [documentation for data providers](/server/framework/atlassian-sdk/adding-data-providers-to-your-plugin/#providing-large-data).

#### `MyDataProvider.java`

```java
package com.example.plugin;

import com.atlassian.json.marshal.Jsonable;
import com.atlassian.json.marshal.wrapped.JsonableString;
import com.atlassian.plugin.clientsideextensions.ExtensionDataProvider;

/**
 * A simple DataProvider class that returns a string as JSON
 */
public class MyDataProvider implements ExtensionDataProvider {
    @Override
    public Jsonable get() {
        return new JsonableString("Hello world!");
    }
}
```

#### `my-extension.ts`

```ts
type DataPayload = 'string';

/**
 * @clientside-extension
 * @extension-point example.extension.point
 * @label "My page"
 * @page-url /my-page
 * @page-title "My page"
 * @page-data-provider com.example.plugin.MyDataProvider
 */
export default PageExtension.factory<DataPayload>((container, data) => {
    container.innerHTML = `<p>${data}</p>`; // Outputs <p>Hello World!</p>
});
```
