# Leaderboard Providers

URL: https://zerodev.ca/docs/medusa/developer-documentation/leaderboards

> Register custom leaderboards, ranking algorithms, and unit formatters.



Modules can declare custom leaderboard providers that automatically integrate with Discord's `/leaderboard` slash command and the web dashboard's Leaderboards view.

Leaderboard providers reside in `resources/leaderboards/` and load from every `*.js` file located directly in that directory.

## Base Class Definition

```js title="modules/example/resources/leaderboards/activity.js"
import { Leaderboards } from "#medusa/modules";

export class ActivityLeaderboard extends Leaderboards {
    constructor(medusa) {
        super(medusa, {
            name: "activity_score",
            label: "Activity Score",
            group: "Community",
            format: "number",
            emoji: "⭐",
            unit: "Points",
            singular: "Point",
            order: 1
        });
    }

    async list(guild) {
        const model = this.medusa.modules.models.get(this.module, "user_scores");
        if (!model) return [];

        const records = await model.list({ guildId: guild.id });
        return records
            .sort((a, b) => b.score - a.score)
            .slice(0, 100)
            .map(record => ({
                userId: String(record.userId),
                value: record.score,
                sub: `Level ${record.level} • Streak: ${record.streak} days`,
                stats: [
                    { label: "Level", value: record.level },
                    { label: "Streak", value: `${record.streak}d` }
                ]
            }));
    }
}
```

## Constructor Options

The configuration object passed to `super(medusa, meta)` accepts the following properties:

| Property   | Type     | Default     | Description                                                             |
| ---------- | -------- | ----------- | ----------------------------------------------------------------------- |
| `name`     | `string` | Required    | Unique leaderboard identifier.                                          |
| `label`    | `string` | `name`      | Display title shown in menus, embeds, and dashboard tables.             |
| `group`    | `string` | `"General"` | Categorization group used to organize multiple leaderboards.            |
| `format`   | `string` | `"number"`  | Value formatting style: `"number"`, `"currency"`, or `"duration"`.      |
| `type`     | `string` | `"card"`    | Default presentation mode: `"card"` or `"embed"`.                       |
| `emoji`    | `string` | `""`        | Emoji prefix displayed next to values.                                  |
| `unit`     | `string` | `""`        | Plural unit name appended to values (e.g. `"Credits"`, `"Messages"`).   |
| `singular` | `string` | `unit`      | Singular unit name used when value is 1 (e.g. `"Credit"`, `"Message"`). |
| `order`    | `number` | `0`         | Sort priority among leaderboards within the same group.                 |

## Supported Formatting Modes

The `format` property determines how Medusa renders values in Discord embeds and dashboard widgets:

| Format Mode  | Sample Output   | Formatting Rules                                                |
| ------------ | --------------- | --------------------------------------------------------------- |
| `"number"`   | `1,250 Points`  | Localized integer formatting with unit suffix.                  |
| `"currency"` | `$ 5,000 Coins` | Prepends emoji or currency symbol and appends unit label.       |
| `"duration"` | `4h 12m`        | Converts numeric seconds into human-readable hours and minutes. |

## The `list(guild)` Method

Every provider must implement `async list(guild)` which receives the active Discord.js `Guild` instance and returns an array of ranked participant objects.

### Return Item Schema

| Property  | Type            | Required | Description                                                                                                 |
| --------- | --------------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `userId`  | `string`        | Yes      | Discord snowflake ID of the member. Used to resolve usernames and avatars.                                  |
| `value`   | `number`        | Yes      | Raw numeric value used for ranking calculations and sorting.                                                |
| `display` | `string`        | No       | Optional pre-formatted text override replacing automatic numeric formatting.                                |
| `sub`     | `string`        | No       | Subtitle text displayed below the user's name in list items.                                                |
| `stats`   | `Array<object>` | No       | Array of supplementary metric objects containing `label` and `value` (string or number) rendered as badges. |

## Leaderboard Manager Methods

The root `this.medusa.leaderboards` service provides methods used across Discord commands and web views:

```js
const allTypes = this.medusa.leaderboards.types();

const rows = await this.medusa.leaderboards.list("activity_score", guild);

const page = this.medusa.leaderboards.paginate(rows, 0, 10);

const position = this.medusa.leaderboards.position(rows, interaction.user.id);
```

## Leaderboard Presentation Types

Medusa supports two presentation formats for leaderboards in Discord:

* **Card (`"card"`)**: Renders a 1024x750 canvas graphic with rank indicators, compact scores, server branding, and member position.
* **Embed (`"embed"`)**: Sends a standard Discord embed with medal emojis and mentionable member strings.

The `/leaderboard` slash command includes an optional `format` option with choices `Card` and `Embed`. When omitted, the command defaults to the server's configured presentation mode (`defaultType` in Core leaderboard configuration), falling back to the provider's descriptor `type` or `"card"`.

Server administrators can configure the default presentation type directly from the top bar of the Leaderboard page in the web dashboard or under **Configs > Core > Leaderboard**.
