Web guide
This guide puts a TaleArt zone on a web page, step by step: the zone key, the script, the element, the options, the hooks, a check on the test page, and six optional steps: a placeholder for a zone that cannot load, a limit on how often a group shows, countdown and promo-code elements, component events, a reaction, and the viewer's properties. The full list of attributes, options, strings, hooks and events is in the Web SDK reference.
Issue the zone key.
On the zone's Install tab in the dashboard, an admin issues the zone key (tak_…). It is shown once: copy it then. Rotating the key issues a new one and revokes the old, for that zone only.
The Install tab with the zone key issued (a real screenshot of the dashboard). The key is shown once, when it is issued, and the card then shows only its first characters. Add the script once per page.
Load the Web SDK from the TaleArt API with one script tag, or install the npm package if your app bundles its own code. The Install tab gives the tag for your API with the zone key filled in. For a pinned, integrity-checked version, see Install a zone.<script type="module" src="https://api.example.com/sdk/v1/taleart.js"></script> <!-- Optional, once per page: <script>Taleart.init({ locale: "en" })</script> --> <taleart-view zone-key="YOUR_ZONE_KEY"></taleart-view>npm i @lempert/taleart import { init } from '@lempert/taleart'; // Optional, once per page: init({ locale: "en" }) <taleart-view zone-key="YOUR_ZONE_KEY"></taleart-view>The two ways to get the SDK. The element is the same for both. Place the zone.
Put one<taleart-view>where the zone should appear, with its zone key. The element renders the zone's display (a bar or a banner) and opens a group full screen when a visitor taps it. Each zone on the page takes its own element and key. The bar's layout (a ring, a chip, a poster or a stack) is set in the dashboard's Display tab, not in the HTML; until the zone arrives the element shows a loading skeleton of that layout (see the layouts).<taleart-view zone-key="YOUR_ZONE_KEY"></taleart-view> <!-- Give the element its height before it renders, so the page does not shift: --> <style> taleart-view { display: block; min-height: 94px; } </style>
A bar, rendered by the Web SDK from a sample zone (a real screenshot of the SDK, not the live API). 
A banner zone, from the same sample zone: its groups as slides. Optional: set options once for the page.
Taleart.init(or the namedinitof the npm package) sets the options for every element: the API origin (apiUrl, needed with npm, where the script has no origin), the locale, the texts (strings), the theme, the viewer's targeting and the hooks. An attribute on one element overrides the page's option. Here the texts are in Hebrew, and the viewer runs right to left.import { init } from 'https://api.example.com/sdk/v1/taleart.js'; init({ locale: 'he', strings: { he: { 'viewer.close': 'סגור', 'viewer.nextGroup': 'הבאה' } }, });
The viewer with locale: 'he', a real screenshot of the SDK. The texts the SDK does not know show English.Listen to the hooks.
The SDK reports what a visitor sees and does: a group coming on screen, a tap on a button, a poll answer, the viewer closing. Use them for analytics. A hook can also stop a link (itspreventDefault()) and route it inside your app: only action clicked can be cancelled. Set hooks for every element withinit, or for one element with itshooksproperty.import { init } from 'https://api.example.com/sdk/v1/taleart.js'; init({ hooks: { // Analytics: one event per group the visitor sees (the analytics object is your own). onGroupImpression: (e) => analytics.track('group_seen', { group: e.group.id }), // Routing: stop the navigation for links to this site and open them in the app. onActionClicked: (e) => { if (e.url.startsWith('https://shop.example.com/')) { e.preventDefault(); router.push(new URL(e.url).pathname); } }, }, });document.querySelector('taleart-view').addEventListener('taleart:action-clicked', (e) => { // The same event as the hook, on the element: call e.preventDefault() to stop the link. console.log(e.detail.url, e.detail.newTab); });The same events are DOM events on the element, bubbling and named
taleart:plus the event in kebab case (taleart:group-impression). Theirdetailis the payload. The full list is in the Web SDK reference.How one event reaches your page. A diagram, not a screenshot. Verify with the test page.
Before you touch your site, open the zone's test page from the Install tab (Open test page). It shows the zone on a page TaleArt serves, with the key in the URL fragment: a browser does not send the fragment to a server, and the page removes it from the address bar. Then open a group, to see the viewer the way a visitor does. If a zone does not show, the zone page says why (Not visible:), and the browser console shows one error for a key that is refused.https://api.example.com/sdk/v1/test-page#zoneKey=YOUR_ZONE_KEY
The test page the Install tab opens, a real screenshot of the page served by the API, with a sample zone. The key is not shown on the page. 
A group open in the viewer, at phone size (a real screenshot of the SDK). Show a placeholder when stories can't load.
When the zone cannot be read (a refused or revoked key, a network failure), the element renders nothing by default. Choose what it shows instead:- Your own markup: a child of the element with
slot="fallback". It is shown in place of the zone, and wins over the next option. - The SDK's text:
fallback="message"on the element, orinit({ fallback: 'message' })for every element. It shows "Stories could not be loaded." and a "Try again" button. - Your code can read the zone again with
retry()on the element. While the zone has failed, the element hasdata-state="failed"; a reload or a successful load removes it.
<!-- Your own placeholder, shown in place of the zone when it cannot be loaded: --> <taleart-view zone-key="YOUR_ZONE_KEY"> <p slot="fallback">Stories are unavailable right now.</p> </taleart-view> <!-- Or the SDK's text and a "Try again" button: --> <taleart-view zone-key="YOUR_ZONE_KEY" fallback="message"></taleart-view>
A slot placeholder, a real screenshot of the SDK: the zone's key is refused, so the card of the page shows in its place. 
fallback="message", a real screenshot of the SDK with the same refused key: its text and a Try again button.- Your own markup: a child of the element with
Limit how often a group shows.
frequencyleaves a tale group out of the display for a viewer after some views, or for a time after a view. Set it on the element, or for every element withinit({ frequency }); the element's attribute wins.- Choose the rule:
once(after the first view),daily(for 24 hours after a view),weekly(for 7 days after a view), or a whole number from 1 to 50 (after that many views, for good). - Watch the cap:
onGroupCappedfires for each group it leaves out, once per render (the DOM event istaleart:group-capped). - Know the limit: views are counted in this browser only, under the local storage key
taleart.views, and never sent to TaleArt. Another browser or device counts its own views.
<!-- Once per group in each browser, for every element of the page: --> <taleart-view zone-key="YOUR_ZONE_KEY" frequency="daily"></taleart-view> <script> Taleart.init({ frequency: 'once', hooks: { onGroupCapped: (e) => console.log('left out', e.groupId, e.rule) }, }); </script>
Before: three groups on the bar, with frequency="once". A real screenshot of the SDK.
After: the Morning group was viewed once, so it is left out. A real screenshot of the SDK, after a reload. - Choose the rule:
Add a countdown or a promo code.
Both are elements of a tale, in the Interactive group of Tale Studio's palette. A countdown shows the days, hours, minutes and seconds to an instant in UTC, counted by the visitor's own clock, and then its end text. A promo code shows a code with a Copy button. The code is visible to every visitor, so it is not a secret. A tale may hold several of each.- In the tale's studio, add Countdown or Promo code from the palette, then set its end time (UTC) or its code in the inspector. Every field and its limit is in the Interactive components page.
- Check it on the test page or in the viewer: the countdown ticks each second, and the promo code has its Copy button.

A countdown in the viewer, a real screenshot of the SDK with a sample tale. 
A promo code in the viewer, a real screenshot of the SDK, before anyone copies it. Listen for component events.
A countdown that ends and a promo code that is copied report a component event: the component's type, the action and, for a copy, the code. Set the hook for every element withinit, or use the DOM event on one element. Poll, quiz, rating and reaction answers report one too, asselected, alongsideonUserInteracted; for a reaction, the value is the emoji tapped.import { init } from 'https://api.example.com/sdk/v1/taleart.js'; init({ hooks: { onComponentEvent: (e) => { // A countdown reached its end, or a visitor copied a promo code (e.value is the code). if (e.component === 'countdown' && e.action === 'ended') analytics.track('countdown_ended', { group: e.groupId }); if (e.component === 'promo-code' && e.action === 'copied') analytics.track('promo_copied', { code: e.value }); }, }, });document.querySelector('taleart-view').addEventListener('taleart:component-event', (e) => { // The same payload as the hook: component, action, and value when there is one. console.log(e.detail.component, e.detail.action, e.detail.value); });
After Copy, a real screenshot of the SDK: the status reads Copied, and the hook has fired with the code. Add a reaction.
A reaction asks one question and offers two to six emoji. A visitor taps one, the tap is counted, and then every emoji shows its count. A tale holds one reaction, and every visitor sees the counts.- In the tale's studio, add Reaction from the Interactive group of the palette. In the inspector, type the question and an optional title, and choose the emoji: two to six, each different. Every field and its limit is in the Interactive components page.
- Check it in the viewer: after a tap, each emoji shows its count and the tapped one is pressed. A visitor can tap once, and the tap is kept on that device, so a reload shows the same counts.
- Count the taps: the inspector's Responses panel lists each emoji with its taps (press Refresh), or read them from the Management API,
GET /tales/{id}/responses. - Listen for a tap: the component event's
componentisreaction, its action isselected, and itsvalueis the emoji.
import { init } from 'https://api.example.com/sdk/v1/taleart.js'; init({ hooks: { onComponentEvent: (e) => { // A visitor tapped an emoji of a reaction: e.value is the emoji. if (e.component === 'reaction' && e.action === 'selected') analytics.track('reaction', { emoji: e.value, group: e.groupId }); }, }, });
A reaction in the viewer before a tap, a real screenshot of the SDK with a sample tale. 
After a tap, a real screenshot of the SDK: each emoji shows its count and the tapped heart is pressed. The counts are the sample's. Show groups by the viewer's properties.
A tale group can carry an audience rule, set in its Audience on the dashboard: for example, a welcome group for viewers on the free plan and an offers group for viewers on the pro plan. The page tells the SDK the viewer's properties once, when the page loads and again after sign-in.setPropertiesreplaces them all,setPropertychanges one and keeps the others, andnullclears. The SDK checks the rule in the browser: the properties are never sent to TaleArt.<script type="module"> import { init, setProperties, setProperty } from 'https://api.example.com/sdk/v1/taleart.js'; // At page load: the properties your site knows about the viewer. Groups with an audience rule are checked against them. init({ properties: { plan: 'free' } }); // After sign-in: the signed-in viewer's properties replace them all. setProperties({ plan: 'pro', age: 34 }); // One property changes later (the viewer upgrades): the others stay. null removes it. setProperty('plan', 'pro'); </script>
A bar for a viewer with plan: 'free': the group for pro viewers is left out.
The same bar for a viewer with plan: 'pro'andage: 34: the welcome group is left out.
A banner for a free viewer: its slides are the groups, so only the welcome slide is there. 
The same banner for a pro viewer: only the pro slide is there.
Right to left
On a Hebrew or Arabic page (<html dir="rtl">) the zone follows the page. The bar starts from the right, the banner's slides and arrows run from the right, and in the viewer the progress, the tap zones, the arrow keys, the swipes and the neighbouring groups turn with it. Arrows and chevrons mirror; play, pause, close, emoji, numbers, countdown digits and promo codes stay as they are. To set the direction for one zone, or for every zone, use dir on the element or init({ dir }); an element's dir wins, and auto (the default) takes the page's.
<!-- The page sets its direction; the zone follows it. For one zone, or for every zone, set it explicitly: -->
<taleart-view zone-key="YOUR_ZONE_KEY" dir="rtl"></taleart-view>
<script>
Taleart.init({ dir: 'rtl' });
</script>
dir="rtl"), a real screenshot of the SDK.

When it is live
The zone shows on the page once the key is in place, the zone has a display, and it has visible content. The zone page's Visible on site says so. A page that places a zone must keep its key: rotating the key stops the old one at once, so update the page first. Read the Web SDK reference for the error behaviour.