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.

  1. 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 zone's Install tab in the dashboard, with its zone key issued: the Zone key card shows the key's first characters and the Rotate and Revoke buttons, and the snippet below it has the script tag, where your own API address appears here.
    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.
  2. 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>
    Two options side by side: a script tag that loads the SDK from the API, or the npm package imported in the app. Both place the same taleart-view element.
    The two ways to get the SDK. The element is the same for both.
  3. 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 rendered bar zone: four circles with group names under them (Summer, New arrivals, Winter, Trips), each with a picture from its first tale.
    A bar, rendered by the Web SDK from a sample zone (a real screenshot of the SDK, not the live API).
    A rendered banner zone: a large picture slide with the group's name and a Watch button, previous and next arrows, and four dots for the slides.
    A banner zone, from the same sample zone: its groups as slides.
  4. Optional: set options once for the page.

    Taleart.init (or the named init of 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 open on a Hebrew group, right to left: the group name at the top right, a close button and a pause button, and the progress bars across the top.
    The viewer with locale: 'he', a real screenshot of the SDK. The texts the SDK does not know show English.
  5. 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 (its preventDefault()) and route it inside your app: only action clicked can be cancelled. Set hooks for every element with init, or for one element with its hooks property.
    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). Their detail is the payload. The full list is in the Web SDK reference.

    Diagram of one event's path: the element calls the element's hooks, then the page's init hooks, then dispatches a DOM event. A host route can call preventDefault to stop a link.
    How one event reaches your page. A diagram, not a screenshot.
  6. 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 TaleArt test page with a zone key from the link: the heading, an empty zone key field, a status line saying the zone is shown with the key from the link, and a bar of three circles named Summer sale, New arrivals and Winter.
    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.
    The viewer open full screen on the Summer group of a phone-sized window: a sunset picture, a progress bar at the top, and the group name, a pause button and a close button.
    A group open in the viewer, at phone size (a real screenshot of the SDK).
  7. 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:
    1. 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.
    2. The SDK's text: fallback="message" on the element, or init({ fallback: 'message' }) for every element. It shows "Stories could not be loaded." and a "Try again" button.
    3. Your code can read the zone again with retry() on the element. While the zone has failed, the element has data-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 home page with the heading Home and a zone that cannot load. In the zone's place is a card with a yellow-to-violet gradient and the text Today's stories are on their way and Check back in a moment, the placeholder from the slot.
    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.
    The same home page with the zone set to fallback message: the text Stories could not be loaded. in the zone's place, and a Try again button below it.
    fallback="message", a real screenshot of the SDK with the same refused key: its text and a Try again button.
  8. Limit how often a group shows.

    frequency leaves 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 with init({ frequency }); the element's attribute wins.
    1. 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).
    2. Watch the cap: onGroupCapped fires for each group it leaves out, once per render (the DOM event is taleart:group-capped).
    3. 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>
    A bar of three circles, each with a picture and a name under it: Morning, Afternoon and Evening. Nothing has been viewed yet.
    Before: three groups on the bar, with frequency="once". A real screenshot of the SDK.
    The same bar after a viewer opened the Morning group and the page was reloaded: the Morning circle is gone, and Afternoon and Evening remain.
    After: the Morning group was viewed once, so it is left out. A real screenshot of the SDK, after a reload.
  9. 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.
    1. 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.
    2. Check it on the test page or in the viewer: the countdown ticks each second, and the promo code has its Copy button.
    A tale in the viewer at phone size: a sunset sale picture with a white card that reads Sale ends in, and the days, hours and minutes left: 2 days, 5 hours and 40 minutes.
    A countdown in the viewer, a real screenshot of the SDK with a sample tale.
    A tale in the viewer at phone size: a teal picture with a gift box, and a white card with the title Your welcome gift, the code SUMMER25 in a dashed chip, its note, and a Copy button.
    A promo code in the viewer, a real screenshot of the SDK, before anyone copies it.
  10. 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 with init, or use the DOM event on one element. Poll, quiz, rating and reaction answers report one too, as selected, alongside onUserInteracted; 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);
    });
    The promo code card after a visitor pressed Copy: the code SUMMER25 in its chip, and the status under the button, Copied. The viewer's header at the top names the group, Welcome gift.
    After Copy, a real screenshot of the SDK: the status reads Copied, and the hook has fired with the code.
  11. 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.
    1. 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.
    2. 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.
    3. 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.
    4. Listen for a tap: the component event's component is reaction, its action is selected, and its value is 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 });
        },
      },
    });
    The viewer at phone size on the Tonight group: a fireworks picture, and a white card with the title Your reaction, the question How do you feel? and three emoji buttons, a red heart, a face with tears of joy and a surprised face, with no counts yet.
    A reaction in the viewer before a tap, a real screenshot of the SDK with a sample tale.
    The same card after a tap on the red heart: each emoji shows its count, 129, 41 and 17, and the heart is pressed, marked by a thicker border and a grey background.
    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.
  12. 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. setProperties replaces them all, setProperty changes one and keeps the others, and null clears. 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 free viewer: only the Welcome group shows, as one round tile.
    A bar for a viewer with plan: 'free': the group for pro viewers is left out.
    A bar for a pro viewer: only the Pro offers group shows, as one round tile.
    The same bar for a viewer with plan: 'pro' and age: 34: the welcome group is left out.
    A banner for a free viewer: the Welcome gift slide, a teal artwork with a gift box.
    A banner for a free viewer: its slides are the groups, so only the welcome slide is there.
    A banner for a pro viewer: the Pro sale slide, a warm sunset artwork with a large sun.
    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>
A bar on a Hebrew page, right to left: four circles with Hebrew group names under them, starting at the right, the first group named קיץ.
The bar on a Hebrew page (dir="rtl"), a real screenshot of the SDK.
A banner on a Hebrew page, right to left: the slide with a sun and its Hebrew name and button at the right, the previous arrow on the right, the next arrow on the left, and the dots with the first one at the right.
The banner on a Hebrew page: the first slide is at the right, and the arrows face the way the slides run.
The viewer on a wide screen on a Hebrew page: the group in the middle as a card, the next tale on its left and the previous one on its right, the previous group's chip at the bottom right and the next group's at the bottom left.
The viewer on a wide screen, right to left: the progress fills from the right, and the group chips sit where the reader expects them.

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.