sveltekit-lanyard Svelte Themes

Sveltekit Lanyard

A Svelte 5 library for interacting with the Discord Lanyard API with type-safe WebSocket and REST support.

sveltekit-lanyard

A modern, type-safe Svelte 5 library for seamless Discord Lanyard API integration with real-time WebSocket and REST support.

Features

  • Fully Type-Safe - Complete TypeScript support with Discord API types
  • Fine-Grained Reactivity - Built with Svelte 5 runes
  • Dual Connection Modes - Choose between WebSocket (real-time) or REST (polling)
  • Automatic Polling - Optional automatic data refreshing for REST connections
  • Auto-Reconnection - Configurable automatic reconnection for WebSocket connections
  • Heartbeat Management - Automatic WebSocket heartbeat handling to keep connections alive
  • Custom URLs - Support for self-hosted Lanyard instances
  • SSR Compatible - Works seamlessly with SvelteKit's server-side rendering

Installation

# pnpm
pnpm add sveltekit-lanyard

# bun
bun add sveltekit-lanyard

Quick Start

REST Mode

<script lang="ts">
    import { useLanyard } from 'sveltekit-lanyard';

    const lanyard = useLanyard({
        connectionType: 'rest',
        userId: '769702535124090904'
    });
</script>

{#if lanyard.loading}
    <p>Loading presence...</p>
{:else if lanyard.data}
    <p>{lanyard.data.discord_user.username} is {lanyard.data.discord_status}</p>
{/if}

WebSocket Mode

<script lang="ts">
    import { useLanyard } from 'sveltekit-lanyard';

    const lanyard = useLanyard({
        connectionType: 'ws',
        subscriptionScope: {
            subscribe_to_id: '769702535124090904'
        }
    });
</script>

{#if lanyard.connected && lanyard.data}
    <p>{lanyard.data.discord_user.username} is {lanyard.data.discord_status}</p>
{/if}

Usage

REST API

The REST API is ideal for simple use cases where you want to fetch presence data on-demand or at intervals.

Basic Example

<script lang="ts">
    import { useLanyard } from 'sveltekit-lanyard';

    const lanyard = useLanyard({
        connectionType: 'rest',
        userId: '94490510688792576'
    });
</script>

<div class="presence-card">
    {#if lanyard.loading}
        <div class="loading">
            <p>Loading presence data...</p>
        </div>
    {:else if lanyard.error}
        <div class="error">
            <p>Error: {lanyard.error.message}</p>
            <button onclick={() => lanyard.refetch()}>Retry</button>
        </div>
    {:else if lanyard.data}
        <div class="user-info">
            <img
                src="https://cdn.discordapp.com/avatars/{lanyard.data.discord_user.id}/{lanyard.data
                    .discord_user.avatar}.png"
                alt="Avatar"
            />
            <h2>{lanyard.data.discord_user.username}</h2>
            <span class="status status-{lanyard.data.discord_status}">
                {lanyard.data.discord_status}
            </span>
        </div>

        <button onclick={() => lanyard.refetch()}>Refresh</button>
    {/if}
</div>

Manual Refetch

<script lang="ts">
    import { useLanyard } from 'sveltekit-lanyard';

    const lanyard = useLanyard({
        connectionType: 'rest',
        userId: '94490510688792576'
    });

    // Refetch every 30 seconds
    $effect(() => {
        const interval = setInterval(() => {
            lanyard.refetch();
        }, 30000);

        return () => clearInterval(interval);
    });
</script>

WebSocket API

The WebSocket API provides real-time updates whenever a user's presence changes on Discord.

Single User Subscription

<script lang="ts">
    import { useLanyard } from 'sveltekit-lanyard';

    const lanyard = useLanyard({
        connectionType: 'ws',
        subscriptionScope: {
            subscribe_to_id: '94490510688792576'
        }
    });
</script>

<div class="presence-realtime">
    {#if !lanyard.connected}
        <p>Connecting to WebSocket...</p>
    {:else if lanyard.error}
        <p class="error">{lanyard.error}</p>
    {:else if lanyard.data}
        <div class="user">
            <h2>{lanyard.data.discord_user.username}</h2>
            <p>Status: {lanyard.data.discord_status}</p>

            {#if lanyard.lastUpdate}
                <p class="timestamp">
                    Last update: {new Date(lanyard.lastUpdate.d.timestamp).toLocaleString()}
                </p>
            {/if}
        </div>
    {/if}
</div>

Multiple Users Subscription

<script lang="ts">
    import { useLanyard } from 'sveltekit-lanyard';

    const lanyard = useLanyard({
        connectionType: 'ws',
        subscriptionScope: {
            subscribe_to_ids: ['94490510688792576', '162619778399240192', '261304190928461824']
        }
    });
</script>

{#if lanyard.connected && lanyard.data}
    <div class="users-grid">
        {#each Object.values(lanyard.data) as user}
            <div class="user-card">
                <h3>{user.discord_user.username}</h3>
                <span class="status-{user.discord_status}">
                    {user.discord_status}
                </span>

                {#if user.listening_to_spotify}
                    <div class="spotify-mini">
                        🎵 Listening to {user.spotify.song}
                    </div>
                {/if}
            </div>
        {/each}
    </div>
{/if}

Subscribe to All Users (Advanced)

<script lang="ts">
    import { useLanyard } from 'sveltekit-lanyard';

    const lanyard = useLanyard({
        connectionType: 'ws',
        subscriptionScope: {
            subscribe_to_all: true
        }
    });
</script>

API Reference

useLanyard(options)

The main function to initialize a Lanyard connection.

Options

REST Mode Options
{
    connectionType: 'rest';
    userId: string; // Discord user ID (Snowflake)
    polling?: boolean; // Enable automatic polling (default: false)
    pollingInterval?: number; // Polling interval in ms (default: 5000)
    apiUrl?: string; // Custom REST API URL (default: 'https://api.lanyard.rest/v1')
}

REST Options:

  • connectionType - Must be 'rest' for REST mode
  • userId - Discord user ID (Snowflake)
  • polling - Optional. Enable automatic polling to refresh data at intervals (default: false)
  • pollingInterval - Optional. Polling interval in milliseconds (default: 5000, only applies when polling is true)
  • apiUrl - Optional. Custom REST API base URL for self-hosted Lanyard instances (default: 'https://api.lanyard.rest/v1')
WebSocket Mode Options
{
  connectionType: 'ws';
  subscriptionScope:
    | { subscribe_to_id: string }       // Single user
    | { subscribe_to_ids: string[] }    // Multiple users (array of user IDs)
    | { subscribe_to_all: true }        // All users
  maxReconnectAttempts?: number;        // Max reconnection attempts (default: 3)
  reconnectDelay?: number;              // Delay between reconnects in ms (default: 1000)
  wsUrl?: string;                       // Custom WebSocket URL (default: 'wss://api.lanyard.rest/socket')
}

WebSocket Options:

  • connectionType - Must be 'ws' for WebSocket mode
  • subscriptionScope - Subscription configuration:
    • subscribe_to_id - Single user ID to subscribe to
    • subscribe_to_ids - Array of user IDs to subscribe to
    • subscribe_to_all - Subscribe to all users (requires special access)
  • maxReconnectAttempts - Optional. Maximum number of automatic reconnection attempts (default: 3)
  • reconnectDelay - Optional. Delay in milliseconds between reconnection attempts (default: 1000)
  • wsUrl - Optional. Custom WebSocket URL for self-hosted Lanyard instances (default: 'wss://api.lanyard.rest/socket')

Return Value

REST Mode Return
{
  data: LanyardPresenceData | null;
  error: { message: string; code: string } | null;
  loading: boolean;
  refetch: () => Promise<void>;
}

Properties:

  • data - The user's presence data, or null if not loaded
  • error - Error object if request failed, or null
  • loading - true during initial load or hydration
  • refetch() - Function to manually refresh the data
WebSocket Mode Return
{
    data: LanyardPresenceData | Record<string, LanyardPresenceData> | null;
    error: string | null;
    connected: boolean;
    initState: InitStatePayload | null;
    lastUpdate: PresenceUpdatePayload | null;
}

Properties:

  • data - Presence data (single user object or multi-user record)
  • error - Error message if connection failed, or null
  • connected - true when WebSocket is connected
  • initState - Initial state received from WebSocket on connection
  • lastUpdate - The most recent presence update event

Advanced Configuration

Automatic Polling (REST)

Enable automatic polling to keep REST data fresh without manual intervention:

<script lang="ts">
    import { useLanyard } from 'sveltekit-lanyard';

    const lanyard = useLanyard({
        connectionType: 'rest',
        userId: '94490510688792576',
        polling: true, // Enable automatic polling
        pollingInterval: 3000 // Poll every 3 seconds (default: 5000ms)
    });
</script>

<div>
    {#if lanyard.data}
        <p>{lanyard.data.discord_user.username} is {lanyard.data.discord_status}</p>
        <p class="info">Data automatically refreshes every 3 seconds</p>
    {/if}

    <!-- Manual refresh is still available -->
    <button onclick={() => lanyard.refetch()}>Refresh Now</button>
</div>

Automatic Reconnection (WebSocket)

WebSocket connections automatically attempt to reconnect when disconnected:

<script lang="ts">
    import { useLanyard } from 'sveltekit-lanyard';

    const lanyard = useLanyard({
        connectionType: 'ws',
        subscriptionScope: {
            subscribe_to_id: '94490510688792576'
        },
        maxReconnectAttempts: 5, // Try up to 5 times (default: 3)
        reconnectDelay: 2000 // Wait 2 seconds between attempts (default: 1000ms)
    });
</script>

<div>
    {#if lanyard.connected}
        <span class="badge badge-success">Connected</span>
    {:else if lanyard.error}
        <span class="badge badge-error">Connection Error</span>
        <p>Attempting to reconnect...</p>
    {:else}
        <span class="badge badge-warning">Connecting...</span>
    {/if}
</div>

Default URLs:

  • REST API: https://api.lanyard.rest/v1
  • WebSocket: wss://api.lanyard.rest/socket

Type Definitions

Core Types

import type {
    LanyardPresenceData,
    LanyardRestApi,
    LanyardWebSocketApi,
    LanyardClient,
    LanyardGeneric
} from 'sveltekit-lanyard';

LanyardPresenceData

type LanyardPresenceData = {
    active_on_discord_mobile: boolean;
    active_on_discord_desktop: boolean;
    active_on_discord_embedded: boolean;
    active_on_discord_web: boolean;
    listening_to_spotify: boolean;
    spotify?: {
        track_id: string;
        song: string;
        artist: string;
        album: string;
        album_art_url: string;
        timestamps: {
            start: number;
            end: number;
        };
    };
    discord_user: {
        id: string;
        username: string;
        avatar: string;
        discriminator: string;
        bot: boolean;
        global_name: string | null;
        avatar_decoration_data: any | null;
        display_name: string | null;
        public_flags: number;
    };
    discord_status: 'online' | 'dnd' | 'idle' | 'offline';
    activities: Activity[];
    kv: Record<string, string>;
};

Activity

Discord activity object following Discord API types.

type Activity = {
    name: string;
    type: number;
    state?: string;
    details?: string;
    timestamps?: {
        start?: number;
        end?: number;
    };
    assets?: {
        large_image?: string;
        large_text?: string;
        small_image?: string;
        small_text?: string;
    };
    // ... and more Discord activity properties
};

Examples

You can check out a few examples in /examples

FAQ

Q: What is Lanyard?

A: Lanyard is a service that exposes Discord presence data through a simple API. You need to join the Lanyard Discord server to use it with your Discord account.

Q: How do I get a Discord user ID?

A: Enable Developer Mode in Discord settings, then right-click a user and select "Copy ID".

Q: How do I handle errors?

A: Both modes expose an error property. For REST, you can retry with refetch(). WebSocket automatically attempts to reconnect based on your maxReconnectAttempts setting.

License

MIT © Mufaro

Credits

Top categories

Loading Svelte Themes