piqo
v0.0.7
Published
Lightweight live chat widget
Maintainers
Readme
PiqoChat Widget
A lightweight, embeddable chat widget similar to Intercom. Built with TypeScript and Vite.
Features
- 🪶 Lightweight - Only ~7.6KB minified (2.4KB gzipped)
- 🔒 Type-safe - Full TypeScript support
- 📱 Responsive - Works on desktop and mobile
- 🎨 Customizable - Easy to style and configure
- 💬 Iframe-based - Secure communication bridge between host page and widget
- 🛡️ Shadow DOM - Completely isolated from page styles, no CSS conflicts
Installation
Quick Start
Add this script tag to your website, just before the closing </body> tag:
<script>
;(function () {
var w = window
var ic = w.PiqoChat
if (typeof ic === 'function') {
ic('update', w.piqoChatSettings)
return
}
var d = document
var i = function () {
i.c(arguments)
}
i.q = []
i.c = function (args) {
i.q.push(args)
}
w.PiqoChat = i
function l() {
var s = d.createElement('script')
s.type = 'text/javascript'
s.async = true
s.src = 'https://unpkg.com/piqo/dist/widget.js'
var x = d.getElementsByTagName('script')[0]
x.parentNode.insertBefore(s, x)
}
if (document.readyState === 'complete') {
l()
} else if (w.attachEvent) {
w.attachEvent('onload', l)
} else {
w.addEventListener('load', l, false)
}
})()
</script>
<script>
PiqoChat('boot', {
workspace: 'your-workspace-id',
apiBase: 'https://app.piqochat.com',
user: {
id: 'user-123',
name: 'John Doe',
email: '[email protected]'
}
})
</script>Framework Integration Guides
Plain HTML
Simply add the scripts to your HTML file before the closing </body> tag:
<!DOCTYPE html>
<html>
<head>
<title>My Website</title>
</head>
<body>
<!-- Your content -->
<script>
;(function () {
var w = window
var ic = w.PiqoChat
if (typeof ic === 'function') {
ic('update', w.piqoChatSettings)
return
}
var d = document
var i = function () {
i.c(arguments)
}
i.q = []
i.c = function (args) {
i.q.push(args)
}
w.PiqoChat = i
function l() {
var s = d.createElement('script')
s.type = 'text/javascript'
s.async = true
s.src = 'https://unpkg.com/piqo/dist/widget.js'
var x = d.getElementsByTagName('script')[0]
x.parentNode.insertBefore(s, x)
}
if (document.readyState === 'complete') {
l()
} else if (w.attachEvent) {
w.attachEvent('onload', l)
} else {
w.addEventListener('load', l, false)
}
})()
</script>
<script>
PiqoChat('boot', {
workspace: 'your-workspace-id',
apiBase: 'https://app.piqochat.com',
user: {
id: 'user-123',
name: 'John Doe',
email: '[email protected]'
}
})
</script>
</body>
</html>Next.js
App Router (Next.js 13+)
Create a component for the PiqoChat widget:
// components/PiqoChat.tsx
'use client'
import { useEffect } from 'react'
declare global {
interface Window {
PiqoChat: any
}
}
export default function PiqoChat() {
useEffect(() => {
// Load the PiqoChat loader script
const loaderScript = document.createElement('script')
loaderScript.innerHTML = `
(function() {
var w = window;
var ic = w.PiqoChat;
if (typeof ic === 'function') {
ic('update', w.piqoChatSettings);
return;
}
var d = document;
var i = function() {
i.c(arguments);
};
i.q = [];
i.c = function(args) {
i.q.push(args);
};
w.PiqoChat = i;
function l() {
var s = d.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'https://unpkg.com/piqo/dist/widget.js';
var x = d.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (document.readyState === 'complete') {
l();
} else if (w.attachEvent) {
w.attachEvent('onload', l);
} else {
w.addEventListener('load', l, false);
}
})();
`
document.body.appendChild(loaderScript)
// Initialize PiqoChat
if (window.PiqoChat) {
window.PiqoChat('boot', {
workspace: 'your-workspace-id',
apiBase: 'https://app.piqochat.com',
user: {
id: 'user-123',
name: 'John Doe',
email: '[email protected]'
}
})
}
return () => {
// Cleanup on unmount
if (window.PiqoChat) {
window.PiqoChat('shutdown')
}
}
}, [])
return null
}Then add it to your layout:
// app/layout.tsx
import PiqoChat from '@/components/PiqoChat'
export default function RootLayout({
children
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
{children}
<PiqoChat />
</body>
</html>
)
}Pages Router (Next.js 12 and below)
Add the script to _document.tsx:
// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
<script
dangerouslySetInnerHTML={{
__html: `
(function() {
var w = window;
var ic = w.PiqoChat;
if (typeof ic === 'function') {
ic('update', w.piqoChatSettings);
return;
}
var d = document;
var i = function() {
i.c(arguments);
};
i.q = [];
i.c = function(args) {
i.q.push(args);
};
w.PiqoChat = i;
function l() {
var s = d.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'https://unpkg.com/piqo/dist/widget.js';
var x = d.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (document.readyState === 'complete') {
l();
} else if (w.attachEvent) {
w.attachEvent('onload', l);
} else {
w.addEventListener('load', l, false);
}
})();
`
}}
/>
<script
dangerouslySetInnerHTML={{
__html: `
PiqoChat('boot', {
workspace: 'your-workspace-id',
apiBase: 'https://app.piqochat.com',
user: {
id: 'user-123',
name: 'John Doe',
email: '[email protected]'
}
});
`
}}
/>
</body>
</Html>
)
}Nuxt.js
Nuxt 3
Create a plugin for PiqoChat:
// plugins/piqochat.client.ts
export default defineNuxtPlugin(() => {
if (process.client) {
// Load the PiqoChat loader script
const loaderScript = document.createElement('script')
loaderScript.innerHTML = `
(function() {
var w = window;
var ic = w.PiqoChat;
if (typeof ic === 'function') {
ic('update', w.piqoChatSettings);
return;
}
var d = document;
var i = function() {
i.c(arguments);
};
i.q = [];
i.c = function(args) {
i.q.push(args);
};
w.PiqoChat = i;
function l() {
var s = d.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'https://unpkg.com/piqo/dist/widget.js';
var x = d.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (document.readyState === 'complete') {
l();
} else if (w.attachEvent) {
w.attachEvent('onload', l);
} else {
w.addEventListener('load', l, false);
}
})();
`
document.body.appendChild(loaderScript)
// Initialize PiqoChat
if (window.PiqoChat) {
window.PiqoChat('boot', {
workspace: 'your-workspace-id',
apiBase: 'https://app.piqochat.com',
user: {
id: 'user-123',
name: 'John Doe',
email: '[email protected]'
}
})
}
}
})Nuxt 2
Create a plugin:
// plugins/piqochat.client.js
export default ({ app }, inject) => {
if (process.client) {
// Load the PiqoChat loader script
const loaderScript = document.createElement('script')
loaderScript.innerHTML = `
(function() {
var w = window;
var ic = w.PiqoChat;
if (typeof ic === 'function') {
ic('update', w.piqoChatSettings);
return;
}
var d = document;
var i = function() {
i.c(arguments);
};
i.q = [];
i.c = function(args) {
i.q.push(args);
};
w.PiqoChat = i;
function l() {
var s = d.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'https://unpkg.com/piqo/dist/widget.js';
var x = d.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (document.readyState === 'complete') {
l();
} else if (w.attachEvent) {
w.attachEvent('onload', l);
} else {
w.addEventListener('load', l, false);
}
})();
`
document.body.appendChild(loaderScript)
// Initialize PiqoChat
if (window.PiqoChat) {
window.PiqoChat('boot', {
workspace: 'your-workspace-id',
apiBase: 'https://app.piqochat.com',
user: {
id: 'user-123',
name: 'John Doe',
email: '[email protected]'
}
})
}
}
}Then register it in nuxt.config.js:
// nuxt.config.js
export default {
plugins: [{ src: '~/plugins/piqochat.client.js', mode: 'client' }]
}React (Vite)
Create a component for PiqoChat:
// src/components/PiqoChat.tsx
import { useEffect } from 'react'
declare global {
interface Window {
PiqoChat: any
}
}
export default function PiqoChat() {
useEffect(() => {
// Load the PiqoChat loader script
const loaderScript = document.createElement('script')
loaderScript.innerHTML = `
(function() {
var w = window;
var ic = w.PiqoChat;
if (typeof ic === 'function') {
ic('update', w.piqoChatSettings);
return;
}
var d = document;
var i = function() {
i.c(arguments);
};
i.q = [];
i.c = function(args) {
i.q.push(args);
};
w.PiqoChat = i;
function l() {
var s = d.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'https://unpkg.com/piqo/dist/widget.js';
var x = d.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (document.readyState === 'complete') {
l();
} else if (w.attachEvent) {
w.attachEvent('onload', l);
} else {
w.addEventListener('load', l, false);
}
})();
`
document.body.appendChild(loaderScript)
// Initialize PiqoChat
if (window.PiqoChat) {
window.PiqoChat('boot', {
workspace: 'your-workspace-id',
apiBase: 'https://app.piqochat.com',
user: {
id: 'user-123',
name: 'John Doe',
email: '[email protected]'
}
})
}
return () => {
// Cleanup on unmount
if (window.PiqoChat) {
window.PiqoChat('shutdown')
}
}
}, [])
return null
}Then add it to your App:
// src/App.tsx
import PiqoChat from './components/PiqoChat'
function App() {
return (
<>
{/* Your app content */}
<PiqoChat />
</>
)
}
export default AppVue (Vite)
Create a component for PiqoChat:
<!-- src/components/PiqoChat.vue -->
<template>
<div></div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
declare global {
interface Window {
PiqoChat: any
}
}
onMounted(() => {
// Load the PiqoChat loader script
const loaderScript = document.createElement('script')
loaderScript.innerHTML = `
(function() {
var w = window;
var ic = w.PiqoChat;
if (typeof ic === 'function') {
ic('update', w.piqoChatSettings);
return;
}
var d = document;
var i = function() {
i.c(arguments);
};
i.q = [];
i.c = function(args) {
i.q.push(args);
};
w.PiqoChat = i;
function l() {
var s = d.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'https://unpkg.com/piqo/dist/widget.js';
var x = d.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (document.readyState === 'complete') {
l();
} else if (w.attachEvent) {
w.attachEvent('onload', l);
} else {
w.addEventListener('load', l, false);
}
})();
`
document.body.appendChild(loaderScript)
// Initialize PiqoChat
if (window.PiqoChat) {
window.PiqoChat('boot', {
workspace: 'your-workspace-id',
apiBase: 'https://app.piqochat.com',
user: {
id: 'user-123',
name: 'John Doe',
email: '[email protected]'
}
})
}
})
onUnmounted(() => {
// Cleanup on unmount
if (window.PiqoChat) {
window.PiqoChat('shutdown')
}
})
</script>Then add it to your App:
<!-- src/App.vue -->
<template>
<div>
<!-- Your app content -->
<PiqoChat />
</div>
</template>
<script setup lang="ts">
import PiqoChat from './components/PiqoChat.vue'
</script>Configuration
Basic Configuration
PiqoChat('boot', {
workspace: 'your-workspace-id', // Required
apiBase: 'https://app.piqochat.com', // Optional - defaults to window.location.origin
user: {
id: 'user-123',
name: 'John Doe',
email: '[email protected]',
// Add any custom properties
customField: 'value'
}
})Custom Styling
PiqoChat('boot', {
workspace: 'your-workspace-id',
customStyles: {
primaryColor: 'rgb(10, 10, 10)',
buttonSize: '3rem',
offset: '20px'
}
})API Methods
boot(config)
Initialize and display the widget.
PiqoChat('boot', {
workspace: 'your-workspace-id',
user: { id: '123', name: 'John' }
})show()
Show the widget.
PiqoChat('show')hide()
Hide the widget.
PiqoChat('hide')update(config)
Update the widget configuration.
PiqoChat('update', {
user: { id: '456', name: 'Jane' }
})shutdown()
Completely remove the widget from the page.
PiqoChat('shutdown')Event Callbacks
// Set callback for when widget is shown
PiqoChat.onShow = function () {
console.log('Widget opened')
}
// Set callback for when widget is hidden
PiqoChat.onHide = function () {
console.log('Widget closed')
}Iframe Communication
The widget uses postMessage API for secure communication between the host page and the iframe.
Messages sent from host to iframe:
boot- Initialize widget with configshow- Show widgethide- Hide widgetupdate- Update configshutdown- Shutdown widget
Messages sent from iframe to host:
ready- Iframe is readyshow- Request to show widgethide- Request to hide widget
Development
Setup
npm installRun dev server
npm run devThis will start the dev server on port 5173 and open the demo page.
Build
npm run buildPreview build
npm run previewHow it works
- Loader Script: A small inline script creates a global
PiqoChatfunction that queues commands - Async Loading: The main widget script loads asynchronously
- Command Processing: Once loaded, the widget processes all queued commands
- Iframe Communication: Widget content loads in an iframe with secure message passing
- Clean API: Simple API for controlling the widget from the host page
License
MIT
