@userband/widget
v0.4.0
Published
Userband Widget SDK - TypeScript loader for embedding Userband feedback widget
Downloads
38
Maintainers
Readme
@userband/widget
TypeScript SDK for seamlessly embedding Userband feedback widget into your web application
✨ Features
- 🚀 Easy Integration - Just a few lines of code to get started
- 📘 TypeScript Native - Full type safety with comprehensive TypeScript definitions
- 🎯 Zero Dependencies - Lightweight SDK with no external dependencies
- 🔄 Auto Loading - Automatically loads and initializes the widget script
- ⚡ Optimized Bundle - Only ~275KB gzipped for initial load with smart code splitting
- 🎨 Rich API - Complete control over widget behavior and appearance
- 📱 Responsive - Works seamlessly across all devices
- 🔐 User Identification - Built-in support for authenticated users
📦 Installation
Using npm:
npm install @userband/widgetUsing pnpm:
pnpm add @userband/widgetUsing yarn:
yarn add @userband/widget🚀 Quick Start
Get up and running in less than a minute:
import Userband from "@userband/widget"
// Initialize the widget
await Userband.init({
clientKey: "YOUR_CLIENT_KEY",
})That's it! The widget will automatically load and appear on your page.
🔧 Configuration
Basic Configuration
import Userband from "@userband/widget"
await Userband.init({
clientKey: "YOUR_CLIENT_KEY", // Required: Your Userband client key
hostUrl: "https://api.userband.com", // Optional: API host URL
showLauncher: true, // Optional: Show the launcher button
debug: true, // Optional: Enable debug logging
})Configuration Options
| Option | Type | Required | Default | Description |
| -------------- | --------- | -------- | -------------------------- | ------------------------------------------------------ |
| clientKey | string | ✅ Yes | - | Your Userband client key from the dashboard |
| hostUrl | string | No | https://api.userband.com | API host URL for your Userband instance |
| widgetUrl | string | No | https://i.userband.com | Widget script URL (for custom hosting) |
| userId | string | No | - | User ID to identify the user on init |
| userTraits | object | No | - | Additional user information (email, name, etc.) |
| showLauncher | boolean | No | true | Whether to show the launcher button |
| debug | boolean | No | Auto-detected | Enable debug mode for logging (auto-detects localhost) |
Getting Your Client Key
- Sign in to your Userband Dashboard
- Navigate to Settings → Widget
- Copy your Client Key
Debug Mode
The widget supports debug mode for development and troubleshooting. When enabled, it logs detailed information to the browser console.
Auto-Detection:
By default, debug mode is automatically enabled in local development environments:
localhost127.0.0.1- Local network IPs (192.168.x.x, 10.x.x.x, etc.)
In production environments, debug mode is automatically disabled.
Manual Control:
You can explicitly enable or disable debug mode:
// Force enable debug mode (useful for staging environments)
await Userband.init({
clientKey: "YOUR_CLIENT_KEY",
debug: true,
})
// Force disable debug mode (useful for local production testing)
await Userband.init({
clientKey: "YOUR_CLIENT_KEY",
debug: false,
})What Debug Mode Shows:
When enabled, you'll see console logs for:
- Widget initialization and loading
- User identification and session updates
- Panel open/close events
- API calls and responses
- Errors and warnings
Example Output:
[Userband Widget] Initializing widget...
[Userband Widget] Widget loaded successfully
[Userband Widget] User identified: user-123
[Userband Widget] Panel opened: home📚 API Reference
Initialization
init(config)
Initializes the widget with your configuration. This must be called before using any other methods.
Parameters:
config(WidgetConfig): Configuration object
Returns: Promise<void>
Example:
try {
await Userband.init({
clientKey: "YOUR_CLIENT_KEY",
hostUrl: "https://api.userband.com",
})
console.log("Widget initialized successfully")
} catch (error) {
console.error("Failed to initialize widget:", error)
}User Identification
identify(distinctId, traits?)
Identifies a user with the widget. Use this to associate feedback and interactions with specific users.
Parameters:
distinctId(string): Unique identifier for the usertraits(UserTraits, optional): Additional user information
Returns: void
Example:
// Basic identification
Userband.identify("user-123")
// With user traits
Userband.identify("user-123", {
email: "[email protected]",
name: "John Doe",
image: "https://example.com/avatar.jpg",
// Any custom properties
plan: "premium",
company: "Acme Inc",
signupDate: "2024-01-15",
})Widget Control
open()
Opens the widget panel.
Returns: void
Example:
Userband.open()close()
Closes the widget panel.
Returns: void
Example:
Userband.close()destroy()
Completely removes the widget from the page.
Returns: void
Example:
Userband.destroy()Launcher Control
launcher.show()
Shows the launcher button.
Returns: void
Example:
Userband.launcher.show()launcher.hide()
Hides the launcher button.
Returns: void
Example:
Userband.launcher.hide()Panel Control
panel.open(pageType, options?)
Opens the panel with a specific page type.
Parameters:
pageType(PageType): Type of page to open'home'- Home page with all features'feedback-create'- Create new feedback'feedback-list'- View user's feedback list'feedback-detail'- Specific feedback detail'changelog-detail'- Specific changelog detail
options(PanelOpenOptions, optional): Additional optionschangelogId(string): Changelog ID when opening changelog-detailfeedbackId(string): Feedback ID when opening feedback-detail
Returns: void
Examples:
// Open home page
Userband.panel.open("home")
// Open feedback creation form
Userband.panel.open("feedback-create")
// Open user's feedback list
Userband.panel.open("feedback-list")
// Open specific feedback detail
Userband.panel.open("feedback-detail", {
feedbackId: "feedback-xyz789",
})
// Open specific changelog
Userband.panel.open("changelog-detail", {
changelogId: "changelog-abc123",
})State Access
state
Gets the current widget state.
Returns: WidgetState
Properties:
isLauncherVisible(boolean): Whether the launcher is visibleisPanelOpen(boolean): Whether the panel is openuser(object): Current user informationuserId(string | undefined): User IDuserTraits(object | undefined): User traits
Example:
const state = Userband.state
console.log("Launcher visible:", state.isLauncherVisible)
console.log("Panel open:", state.isPanelOpen)
console.log("Current user:", state.user.userId)🎯 Usage Examples
React Integration
import { useEffect } from "react"
import Userband from "@userband/widget"
function App() {
useEffect(() => {
// Initialize widget on mount
Userband.init({
clientKey: process.env.REACT_APP_USERBAND_CLIENT_KEY!,
}).catch(console.error)
}, [])
return (
<div>
<button onClick={() => Userband.open()}>
Open Feedback
</button>
</div>
)
}React with User Authentication
import { useEffect } from "react"
import Userband from "@userband/widget"
import { useAuth } from "./auth" // Your auth provider
function App() {
const { user, isAuthenticated } = useAuth()
useEffect(() => {
// Initialize widget
Userband.init({
clientKey: process.env.REACT_APP_USERBAND_CLIENT_KEY!,
}).catch(console.error)
}, [])
useEffect(() => {
// Identify user when authenticated
if (isAuthenticated && user) {
Userband.identify(user.id, {
email: user.email,
name: user.name,
image: user.avatar,
})
}
}, [isAuthenticated, user])
return <YourApp />
}Next.js Integration
// app/layout.tsx
"use client"
import { useEffect } from "react"
import Userband from "@userband/widget"
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
useEffect(() => {
Userband.init({
clientKey:
process.env.NEXT_PUBLIC_USERBAND_CLIENT_KEY!,
hostUrl: process.env.NEXT_PUBLIC_USERBAND_HOST_URL,
}).catch(console.error)
}, [])
return (
<html lang="en">
<body>{children}</body>
</html>
)
}Vue 3 Integration
<script setup lang="ts">
import { onMounted } from "vue"
import Userband from "@userband/widget"
onMounted(async () => {
try {
await Userband.init({
clientKey: import.meta.env.VITE_USERBAND_CLIENT_KEY,
})
} catch (error) {
console.error("Failed to initialize widget:", error)
}
})
</script>
<template>
<div>
<button @click="Userband.open()">Open Feedback</button>
</div>
</template>Vanilla JavaScript
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<button onclick="openWidget()">Open Feedback</button>
<script type="module">
import Userband from "https://cdn.jsdelivr.net/npm/@userband/widget/+esm"
// Initialize
await Userband.init({
clientKey: "YOUR_CLIENT_KEY",
})
// Identify user
Userband.identify("user-123", {
email: "[email protected]",
name: "User Name",
})
// Make globally available
window.openWidget = () => Userband.open()
</script>
</body>
</html>Conditional Loading
import Userband from "@userband/widget"
// Only load in production
if (process.env.NODE_ENV === "production") {
await Userband.init({
clientKey: process.env.USERBAND_CLIENT_KEY!,
})
}Custom Trigger Button
import Userband from "@userband/widget"
import { useEffect } from "react"
function FeedbackButton() {
useEffect(() => {
// Hide default launcher
Userband.launcher.hide()
}, [])
return (
<button
onClick={() => Userband.panel.open("feedback-create")}
className="custom-feedback-button"
>
Send Feedback
</button>
)
}Dynamic User Updates
import Userband from "@userband/widget"
// When user logs in
function onLogin(user) {
Userband.identify(user.id, {
email: user.email,
name: user.name,
plan: user.subscription.plan,
})
}
// When user logs out
function onLogout() {
Userband.destroy()
// Re-initialize as anonymous if needed
await Userband.init({
clientKey: "YOUR_CLIENT_KEY",
})
}🔒 TypeScript Support
This SDK is written in TypeScript and provides complete type definitions out of the box.
Type Imports
import Userband, {
type WidgetConfig,
type UserTraits,
type PageType,
type PanelOpenOptions,
type WidgetState,
type UserbandInterface,
} from "@userband/widget"Type Definitions
WidgetConfig
interface WidgetConfig {
clientKey: string
hostUrl?: string
widgetUrl?: string
userId?: string
userTraits?: Record<string, unknown>
showLauncher?: boolean
debug?: boolean
}UserTraits
interface UserTraits {
email?: string
name?: string
image?: string
[key: string]: unknown // Any custom properties
}PageType
type PageType =
| "home"
| "feedback-create"
| "feedback-list"
| "feedback-detail"
| "changelog-detail"WidgetState
interface WidgetState {
isLauncherVisible: boolean
isPanelOpen: boolean
user: {
userId: string | undefined
userTraits: Record<string, unknown> | undefined
}
}🌍 Environment Variables
For better security and flexibility, use environment variables for your configuration:
React / Vite
VITE_USERBAND_CLIENT_KEY=your_client_key
VITE_USERBAND_HOST_URL=https://api.userband.comawait Userband.init({
clientKey: import.meta.env.VITE_USERBAND_CLIENT_KEY,
hostUrl: import.meta.env.VITE_USERBAND_HOST_URL,
})Next.js
NEXT_PUBLIC_USERBAND_CLIENT_KEY=your_client_key
NEXT_PUBLIC_USERBAND_HOST_URL=https://api.userband.comawait Userband.init({
clientKey: process.env.NEXT_PUBLIC_USERBAND_CLIENT_KEY!,
hostUrl: process.env.NEXT_PUBLIC_USERBAND_HOST_URL,
})Create React App
REACT_APP_USERBAND_CLIENT_KEY=your_client_key
REACT_APP_USERBAND_HOST_URL=https://api.userband.comawait Userband.init({
clientKey: process.env.REACT_APP_USERBAND_CLIENT_KEY!,
hostUrl: process.env.REACT_APP_USERBAND_HOST_URL,
})🐛 Troubleshooting
Widget not loading
Problem: The widget doesn't appear after calling init().
Solution:
try {
await Userband.init({
clientKey: "YOUR_CLIENT_KEY",
})
console.log("Widget initialized successfully")
} catch (error) {
console.error("Initialization error:", error)
// Check:
// 1. Is your clientKey correct?
// 2. Is the hostUrl reachable?
// 3. Check browser console for CORS errors
}TypeScript errors
Problem: TypeScript can't find the module.
Solution: Make sure you have the latest version:
npm install @userband/widget@latestIf still not working, try adding to your tsconfig.json:
{
"compilerOptions": {
"moduleResolution": "bundler",
"types": ["@userband/widget"]
}
}Widget appears but identify() doesn't work
Problem: User identification is not working.
Solution: Make sure to call identify() after the widget is initialized:
// ✅ Correct
await Userband.init({ clientKey: "YOUR_KEY" })
Userband.identify("user-123", { email: "[email protected]" })
// ❌ Wrong - don't call identify before init
Userband.identify("user-123") // This will throw an error
await Userband.init({ clientKey: "YOUR_KEY" })Multiple widget instances
Problem: Widget appears multiple times.
Solution: The SDK uses a singleton pattern. Only call init() once:
// ✅ Correct - initialize once
useEffect(() => {
Userband.init({ clientKey: "YOUR_KEY" })
}, []) // Empty dependency array
// ❌ Wrong - re-initializes on every render
useEffect(() => {
Userband.init({ clientKey: "YOUR_KEY" })
}) // No dependency arrayCSP (Content Security Policy) Issues
If you're using CSP, make sure to allow the widget script and ES modules:
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self' https://i.userband.com; connect-src 'self' https://api.userband.com;"
/>Note: The widget uses ES modules for optimal performance and code splitting. Modern browsers (Chrome 61+, Firefox 60+, Safari 11+) are fully supported.
🔧 Technical Details
Bundle Size & Performance
The widget is optimized for performance with smart code splitting:
- Initial Load: ~275KB gzipped (main bundle)
- Rich Text Editor: ~73KB gzipped (lazy loaded when user creates feedback)
- Changelog Viewer: ~268KB gzipped (lazy loaded when user views changelog)
Most users will only download the initial ~275KB bundle, as the editor and viewer are loaded on-demand.
Browser Compatibility
The widget uses ES modules and supports all modern browsers:
- Chrome 61+
- Firefox 60+
- Safari 11+
- Edge 16+
For legacy browser support, you may need to configure your bundler to transpile the widget code.
How It Works
- SDK loads widget script - The SDK dynamically injects an ES module script tag
- Widget initializes - Main bundle (~275KB) loads and renders the launcher
- Lazy loading - Rich text editor and changelog viewer load only when needed
- Browser caching - All chunks are cached for subsequent visits
📄 License
MIT © Userband
🤝 Support
- 📧 Email: [email protected]
🚀 What's Userband?
Userband is a complete feedback and changelog platform that helps you build better products by understanding your users' needs. Our widget makes it easy to:
- 📝 Collect Feedback - Let users submit feature requests and bug reports
- 📢 Share Updates - Keep users informed with beautiful changelogs
- 💬 Engage Users - Start conversations and build relationships
- 📊 Understand Needs - Analyze feedback to prioritize your roadmap
Made with ❤️ by the Userband team
