Components
Components are versioned chatbot responses. The embed renders the shared confirm@1, choice@1, and card@1 components automatically. Do not register them. Site-owned components such as productPreview@1 use tiketoChat("component", ...); Tiketo supplies the enabled name and schema.
Since v0.3.0, embed-owned labels use context.locale at launch. Values starting with cs select Czech; all other values select English. This applies to generic fallback text and confirm Yes/No buttons. The embed does not translate questions, choices, card copy, or contract fallbackText.
Renderer contract
Section titled “Renderer contract”The renderer receives:
element: an empty mount inside the widget’s open shadow DOM.payload: data validated against the component contract.{ reply }: a one-shot validated reply function.
The chatbot supplies requestId in the payload. The embed validates it, requires the reply to use the same value, and de-duplicates valid replies. reply() handles these checks. Do not replace the ID. Only the first valid reply is sent, including after history is restored.
Create nodes through element.ownerDocument. Page styles do not cross the widget’s shadow boundary. Use inline styles, CSS-in-JS attached to the mount, or a scoped <style> element inside it. Do not query or style other widget internals.
Return a cleanup function for listeners, timers, injected styles, and framework roots. The widget calls it when the response unmounts. Register the renderer before the response appears. A component restored before registration keeps its fallback text.
Complete productPreview example
Section titled “Complete productPreview example”tiketoChat("component", "productPreview", function (element, payload, api) { var ownerDocument = element.ownerDocument; var card = ownerDocument.createElement("article"); var image; var cleanups = []; var replied = false;
card.style.cssText = "display:grid;gap:12px;padding:16px;border:2px solid #000050;border-radius:16px";
if (payload.imageUrl) { image = ownerDocument.createElement("img"); image.src = payload.imageUrl; image.alt = payload.name; image.width = 640; image.height = 360; image.loading = "lazy"; image.style.maxWidth = "100%"; var removeBrokenImage = function () { image.remove(); }; image.addEventListener("error", removeBrokenImage); cleanups.push(function () { image.removeEventListener("error", removeBrokenImage); }); card.appendChild(image); }
var heading = ownerDocument.createElement("h3"); heading.textContent = payload.name; card.appendChild(heading);
var productId = ownerDocument.createElement("p"); productId.textContent = "Product: " + payload.productId; card.appendChild(productId);
var price = ownerDocument.createElement("p"); price.textContent = new Intl.NumberFormat(ownerDocument.documentElement.lang || "en", { style: "currency", currency: payload.currency, }).format(payload.price); card.appendChild(price);
var buttons = []; payload.variants.forEach(function (variant) { var button = ownerDocument.createElement("button"); button.type = "button"; button.textContent = "Choose " + variant; var choose = function () { if (replied) return; replied = true; buttons.forEach(function (item) { item.disabled = true; }); api.reply({ requestId: payload.requestId, productId: payload.productId, variant: variant, }); }; button.addEventListener("click", choose); cleanups.push(function () { button.removeEventListener("click", choose); }); buttons.push(button); card.appendChild(button); });
element.appendChild(card); return function cleanup() { cleanups.forEach(function (remove) { remove(); }); element.replaceChildren(); };});price is a number and currency is CZK or EUR. Format both with Intl.NumberFormat and your page locale. When present, imageUrl is restricted to HTTPS or a safe root-relative path. The example sets image dimensions, loads lazily, and removes a broken image. Your site owns the component’s visual design.
Use textContent or framework escaping for payload data. Use accessible controls for interaction.
Shared card shape
Section titled “Shared card shape”card@1 requires title and text. imageUrl and link are optional. When present, link must contain both link.url and link.label. The generated catalog marks these fields as “yes, when parent is present.”
Images, links, and fallback
Section titled “Images, links, and fallback”Image and link fields accept absolute https: URLs or safe root-relative paths such as /images/product.svg. The embed rejects protocol-relative URLs, traversal segments, other protocols, and malformed URLs. Root-relative component assets resolve on your website.
If a component is unknown, disabled, unregistered, invalid, or throws while rendering, the widget shows a valid payload fallbackText. Otherwise it uses the generic fallback for the selected locale. Agree on useful fallback copy with Tiketo when designing the contract.