Skip to content

mufarodev/sveltekit-lanyard

Repository files navigation

sveltekit-lanyard

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

npm version License: MIT

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

About

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

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors