@trooply/search-widget
v1.2.0
Published
Drop-in visual + text search widget for e-commerce storefronts. Public-key auth, origin binding, quick-view + add-to-cart events, 9 locales.
Maintainers
Readme
@trooply/search-widget
Drop-in visual + text search widget for e-commerce storefronts, by Trooply.
- Search by typing, by uploading an image, or by pasting an image URL
<trooply-search-widget>custom element — framework-agnostic- Public-key auth + Origin allow-list (embed in page source safely)
- Quick-view + add-to-cart buttons (dispatches
trooply:add-to-cartCustomEvent) - 9-locale UI (en, es, fr, de, pt, it, ja, hi, zh)
Install
npm install @trooply/search-widgetOr pin to the CDN directly without npm:
<script src="https://search.trooply.ai/widget/v1/search-widget.js"
data-client-id="client_..."
data-public-key="pk_live_..."></script>Both paths deliver the same widget. The npm package is for framework builds (Next.js, Vite, Nuxt, SvelteKit, etc.) where you prefer to bundle.
Quick start
React / Next.js
'use client';
import { useEffect, useRef } from 'react';
import { mount, unmount } from '@trooply/search-widget';
export function SearchWidget() {
const ref = useRef(null);
useEffect(() => {
let el;
mount({
clientId: 'client_abc123',
publicKey: 'pk_live_54f9...',
mount: '#trooply-search',
maxResults: 10,
theme: 'light',
locale: 'en',
}).then(node => { el = node; });
return () => el && unmount(el);
}, []);
return <div id="trooply-search" ref={ref} />;
}Vue 3
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import { mount, unmount } from '@trooply/search-widget';
const host = ref(null);
let el = null;
onMounted(async () => {
el = await mount({
clientId: 'client_abc123',
publicKey: 'pk_live_54f9...',
mount: '#trooply-search',
});
});
onUnmounted(() => el && unmount(el));
</script>
<template><div id="trooply-search" ref="host" /></template>Vanilla
<div id="trooply-search"></div>
<script type="module">
import { mount } from '@trooply/search-widget';
mount({
clientId: 'client_abc123',
publicKey: 'pk_live_54f9...',
mount: '#trooply-search',
});
</script>Web Component (no JS API)
After import '@trooply/search-widget', the custom element is registered and you can use it directly:
<trooply-search-widget
data-client-id="client_abc123"
data-public-key="pk_live_54f9..."
data-mount="#trooply-search"
data-max-results="10">
</trooply-search-widget>Configuration
Every config key in mount({...}) maps to a data-* attribute on the underlying custom element. Camel case is converted to kebab case (maxResults → data-max-results).
| Key | Required | Default | Notes |
|-----------------|----------|--------------------------------|-------|
| clientId | yes | — | From your portal (client_...) |
| publicKey | recommended | — | pk_live_..., origin-bound. Safe in page source. |
| clientSecret | no | — | Deprecated. Use publicKey. |
| apiBase | no | https://search.trooply.ai | Override for self-host / staging |
| mount | no | — | CSS selector for inline mount. Omit for floating button. |
| position | no | bottom-right | bottom-right | bottom-left |
| accentColor | no | #6366f1 | Hex color for button + accents |
| buttonSize | no | medium | small | medium | large |
| theme | no | light | light | dark |
| locale | no | auto (from <html lang>) | en / es / fr / de / pt / it / ja / hi / zh |
| maxResults | no | 12 | Cap on results shown |
| productUrl | no | — | Template with {product_id} |
| quickViewUrl | no | — | Template with {product_id} — shows Quick view |
| showAddToCart | no | false | Show Add-to-cart button |
Events
If showAddToCart: true, clicking Add-to-cart dispatches a bubbling, composed CustomEvent that your storefront code can hook into:
window.addEventListener('trooply:add-to-cart', (event) => {
const { product_id, metadata, quantity } = event.detail;
// call your own cart protocol here
myCart.add(product_id, quantity);
// calling event.preventDefault() suppresses the built-in pulse animation
});Authentication model
Public keys (pk_live_...) are the recommended path:
- Generate at
/portal/widget→ Public keys. - Bind each key to an allow-list of up to 50 origins (exact string match).
- Safe to commit to a public repo or paste into a page source — the origin binding does the heavy lifting.
- Scope: read-only search endpoints (
/v1/widget/search/*). Never index / delete / manage.
Server secrets (sk_live_..., data-client-secret) still work for backwards compatibility but log a loud console warning. Rotate to a public key when you can.
Versioning
- The package version is the widget version. Major bumps are breaking.
- The CDN path is major-versioned:
/widget/v1/search-widget.js. When we cut v2, the v1 path keeps serving the last v1 build so pinned embedders don't break. - Read the loaded version from
window.TrooplyWidget.version.
SSR safety
import '@trooply/search-widget' is side-effect-free in Node. The widget installs only when mount() or loadWidget() runs in a browser. Use a useEffect / onMounted / dynamic import pattern with { ssr: false } where your framework needs it.
License
MIT © Trooply
