We’ve migrated our documentation to a new site, which means some URLs have changed.
Subscriptions

Technical Overview

We recommend using one of these integration types for Lightweight Templates for optimal results.

1 - Composer
Should work out of the box without any changes.

2 - Embedded code (tinypass.min.js)
Should work as it did before.

const documentReady = new Promise((resolve) => {
  if (document.readyState === 'complete' || document.readyState === 'interactive') {
    resolve();
  } else {

    document.addEventListener('DOMContentLoaded', resolve);
  }
});
const loadScript = src => {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script')
    script.type = 'text/javascript'
    script.onload = resolve
    script.onerror = reject
    script.src = src
    script.type = "module"
    document.head.append(script)
  })
}

const applicationId = "7bFLYKurpu";
const offerId = "OF14JI47IM9O";
const elementIdOffer = "eb5e49ed-8af7-457f-8307-ae8a018f3f8a";
const templateIdOffer = "GT4XXVBFADZU";
const elementOfferName = "Offer";
const containerSelector = ".container-1";
const predefinedData = {};
const modifiers = {};

Promise.all([documentReady, loadScript("https://cdn.tinypass.com/api/tinypass.min.js")])
  .then(()=>{
      tp = window["pn"] || [];
      tp.push(["setAid", applicationId]);
      tp.push(["setEndpoint", "https://buy.tinypass.com/api/v3"]);
      tp.push(['setUsePianoIdUserProvider', true ]);
      tp.push(['setPianoIdUrl', '/']);
      tp.push(['init', () => {
          var config = {
            offerId,
            templateId: templateIdOffer,
            displayMode: 'inline',
            containerSelector,
            engine: 'LIGHTWEIGHT',
          };
          tp.offer.show(config);
      }]);
  })

Encapsulation type:

  • DOM (plain in embedded code) – The template will be inserted into the DOM tree of the site as a regular HTML element.

  • Shadow DOM – The template will be inserted into the DOM tree of the site as a shadow element.

  • iframe – The template will be inserted into the DOM tree of the site as an iframe.

Display mode:

  • modal – The template will be rendered as a modal.

  • inline – The template will be rendered as part of the page content.

Reactivity in Lightweight templates

Reactivity keeps the user interface (UI) and state in sync, which reduces the need for manual updates.

Signals

Signals serve as core elements in reactive systems. To create a signal, use createSignal. createSignal returns two functions: a getter and a setter.

// JS tab
const [signalValue, updateSignalValue] = createSignal();
// signalValue getter, updateSignalValue setter
// you can pass default value to createSignal function
const [title, updateTitle] = createSignal('Title text');
const [list, updateList] = createSignal([
  { id: 1, text: "Lorem Ipsum 1" },
  { id: 2, text: "Lorem Ipsum 2" }
]);
// HTML | JSX tab
<div class="title">{title()}</div>

If the title is updated, the view will be updated automatically.

// JS tab
const [title, updateTitle] = createSignal('Title text');
setTimeout(() => updateTitle("New title text"), 500);

You can pass a function inside updateTitle which contains the old value as its first argument:

const [title, updateTitle] = createSignal('Title text');
setTimeout(() => updateTitle((oldTitle) => `${oldTitle} and new changes`), 500);

If you need to perform a side effect on a signal change, you can use createEffect:

const [title, updateTitle] = createSignal('Title text');
setTimeout(() => updateTitle("New title text"), 500);

createEffect(() => {
  console.log(title()); // will log if title changes
});

More information about signals.

createEffect

Effects are functions that are triggered when the signals they depend on change. They play a crucial role in managing side effects, which are actions that occur outside of the application's scope, such as DOM manipulations, data fetching, and subscriptions.

const [width, setWidth] = createSignal(0);

createEffect(() => {
  console.log(width()); // will run every time width changes
});

In this example, an effect is created that logs the current value of width to the console. When the value of width changes, the effect is triggered, causing it to run again and log the new value of width.

More information about effects.

onMount

In situations where you just want to run a side effect once, you can use the onMount function. This lifecycle function is similar to an effect, but it does not track any dependencies. Rather, once the component has been initialized, the onMount callback will be executed and will not run again.

createEffect(() => {
  data(); // will run every time data changes
});

onMount(() => {
  // will run only once, when the component is mounted
 runOnInitOnly();
});

onCleanup

While onMount is useful for running a side effect once, onCleanup is helpful for cleaning up a task when it is no longer needed. onCleanup will run whenever the component unmounts, removing any subscriptions that the effect has.

const [count, setCount] = createSignal(0);

const timer = setInterval(() => {
  setCount((prev) => prev + 1);
}, 1000);

onCleanup(() => {
  clearInterval(timer);
});

In this example, the onCleanup function is used to clear the interval that is set up in the effect. To prevent the interval from running indefinitely, the onCleanup function clears the interval once the component unmounts.

onCleanup can be used to prevent memory leaks. These occur when a component is unmounted, but references to it still exist and, as a result, processes could still be running in the background. Using onCleanup to remove any subscriptions or references to the component can help prevent this issue.

createMemo

A memo is created using the createMemo function. Within this function, you can define the derived value or computations you wish to memoize. When called, createMemo will return a getter function that reads the current value of the memo:

const [count, setCount] = createSignal(0)

const isEven = createMemo(() => count() % 2 === 0)

console.log(isEven()) // true

setCount(3)
console.log(isEven()) // false

While memos look similar to effects, they are different in that they return a value. This value is the result of the computation or derived state that you wish to memoize.

More information about memo.

Working with Lightweight templates

Templates do not use default HTML; they use JSX. JSX is a mix of HTML and JavaScript that allows you to interact effectively with HTML elements.

1. How to get an HTML element

// HTML | JSX tab
<div ref={elementVariable}>html element</div>

HTML elements support the ref attribute, which takes the name of the variable to which the element will be saved.

// JS tab
let elementVariable; // contains div element

Not recommended:

// HTML | JSX tab
<div id="elementId">html element</div>

// JS tab
const elementVariable = document.querySelector('#elementId');

2. How to set an event handler on an HTML element

// HTML | JSX tab
<div onClick={clickHandler}> my html element</div>

// JS tab
const clickHandler = (event) => {
// handler logic
}

You don't need to reference the element itself; you just add the onClick attribute to the HTML element and set a handler as the value of the attribute.

List of events.

Not recommended:

// HTML | JSX tab
<div id="elementId">html element</div>

// JS tab
const elementVariable = document.querySelector('#elementId');
elementVariable.addEventListener('click', clickHandler);

const clickHandler = (event) => {
// handler logic
}

// and if this element is destroyed in the future, you need to remove the listener.
elementVariable.removeEventListener('click', clickHandler);

3. How to set a class on an HTML element

JSX provides class and classList attributes.

class - The class attribute allows you to style one or more elements through CSS rules. This provides a more structured approach to styling, as it allows you to reuse styles across multiple elements

classList - When you want to apply multiple classes to an element, you can use the classList attribute. To use it, you can pass either a string or an object where the keys represent the class names and the values represent a boolean expression. When the value is true, the class is applied; when false, it is removed.

// HTML | JSX tab
<div
class="class-name"
classList={{
'class-name--selected': isSelected,
'class-name--disabled': isDisabled
...
}}
>
html element
<div class={redText}></div>
<div class={theme() === "light" ? "light-theme" : "dark-theme"}>
This div's theme is determined dynamically!
</div>
</div>

// JS tab
const isSelected = true;
const isDisabled = false;
const redText = "color: red";
const [theme, setTheme] = createSignal("light");

Not recommended:

// HTML | JSX tab
<div id="elementId">html element</div>

// JS tab
const elementVariable = document.querySelector('#elementId');
const isSelected = true;
const isDisabled = false;if (isSelected) {
elementVariable.classList.add('class-name--selected');
}

if (isDisabled) {
elementVariable.classList.add('class-name--disabled');
}

4. How to set a style on an HTML element

The style attribute allows you to style a single element and define CSS variables dynamically during runtime. To use it, you can pass either a string or an object.

// HTML | JSX tab

// String
<div style="color: red;">This is a red div</div>

// Object
<div style={{ color: "red" }}>This is a red div</div>

Not recommended:

// HTML | JSX tab
<div id="elementId">html element</div>

// JS tab
const elementVariable = document.querySelector('#elementId');
elementVariable.style.color = "red";

5. How to render lists

For lists, we provide a For component:

// HTML | JSX tab
<ul class="term-list">
<For each={termList}>
{(item, index) => {
return (
<li class="term-list-item">
<div class="term-list-item-name">{item.termName}</div>
<div class="term-list-item-body">{item.description}</div>
</li>
)}
}
</For>
</ul>

// JS tab
const termList = [
{
id: 1,
termName: "Term 1",
description: "Lorem Ipsum",
},
...
];

6. How to change a list item state

For example, we need to select a term item on click and add a border to the selected term:

// HTML | JSX tab
<ul class="term-list">
<For each={termList}>
{(item, index) => {
return (
<li
class="term-list-item"
onClick={() => selectTerm(item.id)}
classList={{'term-list-item--selected': item.selected}}
>
<div class="term-list-item-name">{item.termName}</div>
<div class="term-list-item-body">{item.description}</div>
</li>
)}
}
</For>
</ul>

// JS tab
const [termList, updateTermList] = createSignal([
{
id: 1,
termName: "Term 1",
description: "Lorem Ipsum",
selected: false
},
...
]);const selectTerm = (termId) => {
const updatedTermList = termList.map((term) => {
if (term.id === termId) {
term.selected = true;
} else {
term.selected = false;
}
});
updateTermList(updatedTermList);
};

Not recommended:

// HTML | JSX tab
<ul class="term-list">
<For each={termList}>
{(item, index) => {
return (
<li class="term-list-item">
<div class="term-list-item-name">{item.termName}</div>
<div class="term-list-item-body">{item.description}</div>
</li>
)}
}
</For>
</ul>

// JS tab
const [termList, updateTermList] = createSignal([
{
id: 1,
termName: "Term 1",
description: "Lorem Ipsum",
},
...
]);const selectTerm = (event) => {
const clickedTermElement = event.target;

termElements.forEach((termElement) => {
if (termElement !== clickedTermElement) {
termElement.classList.remove('term-list-item--selected');
} else {
termElement.classList.add('term-list-item--selected');
}
});
}

const termElements = Array.from(document.querySelectorAll(".term-list-item"));
termElements.forEach(element => element.addEventListener('click', selectTerm));

// and if this element is destroyed in the future, you need to remove the listener.
termElements.forEach(element => element.removeListener('click', selectTerm));

7. Hide/show an HTML element on click

// HTML | JSX tab
<div class="accordion" classList={{"accordion--open": isAccordionOpen()}}>
<div class="accordion-header" onClick={() => toggleAccordion()}></div>
<Show when={isAccordionOpen()}>
<div class="accordion-body">Content to show/hide</div>
</Show>
</div>

// JS tab
const [isAccordionOpen, setIsAccordionOpen] = createSignal(false);const toggleAccordion = () => {
if (isAccordionOpen()) {
setIsAccordionOpen(false);
} else {
setIsAccordionOpen(true);
}
// you can simplify to setIsAccordionOpen(!isAccordionOpen());
}

8. How to store/update form data

// HTML | JSX tab
<input
onChange={(e) => handleFormDataChange('checkboxValue', e.target.checked)}
type="checkbox"
checked={formData().checkboxValue}
/>
<input
onInput={(e) => handleFormDataChange('inputValue', e.target.value)}
value={formData().inputValue}
/>
// show if checkboxValue checked
<Show when={formData().checkboxValue}>
<div classList={{"long-text": formData().inputValue.length > 10}}>
Some text with "long-text" class if length of input > 10
</div>
</Show>

// JS tab
const [formData, updateFormData] = createSignal({
checkboxValue: true, // default value
inputValue: 'default input text' // default value
});const handleFormDataChange = (fieldName, newData) => {
updateFormData(oldFormData => {
return {...oldFormData, [fieldName]: newData}
});
}

// show form data
createEffect(() => console.log(formData()));

9. Comments

In the HTML | JSX tab you need to use two types of commenting:

  1. // commented code for code inside {} — Ctrl+/

  2. {/* commented code */} for tags and attributes — Ctrl+Shift+/

// HTML | JSX tab
<div>
{/* <div>commented tag</div> */}
</div>
<div
id="container"
// class="class-name"
>
Text
</div>

The following will break a template:

<!-- <div>text</div> -->

10. Fragments

Wrap elements in <Fragment> to group them together in situations where you need a single element. Grouping elements in Fragment has no effect on the resulting DOM; it is the same as if the elements were not grouped. The empty JSX tag <></> is shorthand for <Fragment></Fragment> in most cases.

<>
<div>First tag</div>
<div>Second tag</div>
</>

For example, we can use it in the fallback of a Show component:

<Show when={false} fallback={
<>
<div>First tag</div>
<div>Second tag</div>
</>
}>
Text
</Show>

11. Close tags

In JSX, it is very important to close all tags, even those that can be written without a closing tag in HTML.

  • <area>

  • <base>

  • <br>

  • <col>

  • <embed>

  • <hr>

  • <img>

  • <input>

  • <link>

<i​mg sr​c="source.com" />
<in​put />

<i​mg sr​c="source.com">
<in​put>

Last updated: