Widget Installation
Add the PocketPing chat widget to your website. Choose the method that fits your stack.
| Method | Best For | Features |
|---|---|---|
| CDN | Static sites, landing pages, quick prototypes | 2 lines, no build |
| npm/yarn/pnpm | SPAs, production apps | Full control, TypeScript, tree-shaking |
| Framework | React/Next, Vue/Nuxt, Angular | Type safety, component lifecycle |
Option 1: CDN (Quickest)
One-line install (SaaS)
The simplest way - just add your project key:
<script src="https://cdn.pocketping.io/widget.js" data-project-id="proj_xxxxxxxxxxxxx"></script>
That's it! The widget auto-initializes with your project ID from the dashboard.
With custom options
For more control, initialize manually:
<!-- Step 1: Load the widget -->
<script src="https://cdn.pocketping.io/widget.js"></script>
<!-- Step 2: Initialize -->
<script>
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx', // From dashboard
operatorName: 'Support Team',
});
</script>
The CDN always serves the latest version. You automatically get new features and bug fixes.
Alternative CDN options
<!-- Pin to specific version (no auto-updates) -->
<script src="https://cdn.pocketping.io/[email protected]"></script>
<!-- Alternative: jsdelivr or unpkg -->
<script src="https://cdn.jsdelivr.net/npm/@pocketping/widget/dist/pocketping.min.global.js"></script>
<script src="https://unpkg.com/@pocketping/widget/dist/pocketping.min.global.js"></script>
SaaS vs Self-Hosted
// SaaS users (app.pocketping.io)
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx', // Get from dashboard
operatorName: 'Support Team',
});
// Self-hosted users
PocketPing.init({
endpoint: 'https://yoursite.com/pocketping', // Your bridge server
operatorName: 'Support Team',
});
Option 2: npm / yarn / pnpm
Install the package:
# npm
npm install @pocketping/widget
# yarn
yarn add @pocketping/widget
# pnpm
pnpm add @pocketping/widget
Initialize in your JavaScript/TypeScript:
import PocketPing from '@pocketping/widget';
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
primaryColor: '#6366f1',
theme: 'auto',
position: 'bottom-right',
});
TypeScript Support
Full TypeScript definitions included:
import PocketPing, { type PocketPingConfig } from '@pocketping/widget';
const config: PocketPingConfig = {
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
primaryColor: '#6366f1',
theme: 'auto',
position: 'bottom-right',
welcomeMessage: 'Hi! How can we help?',
};
PocketPing.init(config);
Framework Integrations
React / Next.js
'use client';
import { useEffect } from 'react';
import PocketPing from '@pocketping/widget';
export default function RootLayout({
children
}: {
children: React.ReactNode
}) {
useEffect(() => {
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
primaryColor: '#6366f1',
});
// Note: Don't call destroy() in cleanup - the widget manages its own lifecycle
}, []);
return (
<html>
<body>{children}</body>
</html>
);
}
import { useEffect } from 'react';
import PocketPing from '@pocketping/widget';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
useEffect(() => {
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
});
// Note: Don't call destroy() in cleanup - the widget manages its own lifecycle
}, []);
return <Component {...pageProps} />;
}
The widget uses beforeunload to send disconnect notifications when visitors leave. If you call PocketPing.destroy() in React's cleanup function, it may remove these listeners before the browser fires the event, preventing disconnect notifications from being sent.
The widget manages its own lifecycle - just call init() once and let it handle cleanup automatically.
Vue.js / Nuxt
<script setup>
import { onMounted } from 'vue';
import PocketPing from '@pocketping/widget';
onMounted(() => {
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
primaryColor: '#6366f1',
});
// Note: Don't call destroy() in onUnmounted - the widget manages its own lifecycle
});
</script>
// Note: .client.ts ensures this only runs in browser
import PocketPing from '@pocketping/widget';
export default defineNuxtPlugin(() => {
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
});
});
Angular
import { Component, OnInit } from '@angular/core';
import PocketPing from '@pocketping/widget';
@Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>',
})
export class AppComponent implements OnInit {
ngOnInit() {
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
primaryColor: '#6366f1',
});
// Note: Don't call destroy() in ngOnDestroy - the widget manages its own lifecycle
}
}
Svelte / SvelteKit
<script>
import { onMount } from 'svelte';
import PocketPing from '@pocketping/widget';
import { browser } from '$app/environment';
onMount(() => {
if (browser) {
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
});
// Note: Don't call destroy() in onDestroy - the widget manages its own lifecycle
}
});
</script>
<slot />
Verify Installation
After adding the widget, you should see a chat bubble in the bottom-right corner of your page (or wherever you configured it).
Check the Console
Open DevTools (F12) → Console. You should see:
[PocketPing] Initialized successfully
[PocketPing] Connected to bridge server
Test the Chat
- Click the chat bubble
- Type a message
- If you've connected a bridge (Telegram/Discord/Slack), the message appears there
- Reply from your messaging app
- The reply shows in the widget instantly
Troubleshooting
Widget not appearing?
| Issue | Solution |
|---|---|
| Script not loading | Check Network tab for 404 errors |
| JavaScript error | Check Console tab for errors |
| Z-index issue | Widget may be behind other elements. Add .pocketping-widget { z-index: 99999 !important; } |
| CSS conflict | Check if any global styles affect position: fixed elements |
Console shows error?
| Error | Cause | Solution |
|---|---|---|
Invalid projectId | Wrong or missing project ID | Check dashboard for correct ID |
Failed to connect | Network issue or server down | Check internet connection |
CORS error | Self-hosted backend CORS issue | Add your domain to allowed origins |
CORS errors (self-hosted)?
If self-hosting, configure your bridge server to allow requests from your domain:
// In your bridge server config
{
cors: {
origin: ['https://yoursite.com', 'http://localhost:3000'],
credentials: true,
}
}
Widget appears but no connection?
Checklist:
- Project ID is correct (matches dashboard)
- Bridge server is running (if self-hosted)
- At least one bridge is connected (Telegram/Discord/Slack)
- No firewall blocking HTTP / SSE connections
- Browser allows third-party cookies (if using CDN)
Conditional Loading
Show only on certain pages
// Only show on /pricing and /contact pages
if (['/pricing', '/contact'].includes(window.location.pathname)) {
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
});
}
Show based on user role
// Only show for non-logged-in users
if (!window.currentUser) {
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
});
}
Lazy load after delay
// Load widget 5 seconds after page load
setTimeout(() => {
const script = document.createElement('script');
script.src = 'https://cdn.pocketping.io/widget.js';
script.onload = () => {
PocketPing.init({
projectId: 'proj_xxxxxxxxxxxxx',
operatorName: 'Support Team',
});
};
document.body.appendChild(script);
}, 5000);
Next Steps
- Configuration - All available options (colors, messages, position)
- Custom Events - Track user actions
- Connect Telegram - Start receiving messages