Controls how HTML is going to be inserted into your page. Should be set on an origin element. Can be one of the following keywords:

Strategy Description
replace replace target element with an incoming element (default)
inner replaces target's children with an incoming element
prepend inserts incoming element as a first child of the target
append inserts incoming element as a last child of the target
beforebegin inserts incoming element before target
afterend inserts incoming element after target
morph morphs incoming element into target (see lower)
morph-all same as morph, but does not skip document.activeElement when changing elements
skip just skip that response, sometimes useful for operations with side-effects


With the exception of morph, morph-all (see further) and skip strategies, TwinSpark will try to “settle” all elements with an id attribute.

New elements with an id will be inserted with ts-insert class, which is then removed. Existing elements will be inserted with an old values in settle attibutes (by default this is class, style, width and height), and then given new values shortly afterwards.

This makes transitions work: same element changes from old to new values, which triggers transitions.


Morph is a complex algorithm, and it could take tens of ms on complex layouts, but it could be really useful at times.

Main idea is to keep elements with id in place, only updating their attributes, so that all the browser state (focus, transitions, animations, playing state of videos and audios) is kept intact. This makes doing form validation and various animations easy.

Every new element with an id attribute is insert with a ts-insert class. It is removed after a short timeout, so you can add some transitions on that. Same with elements being removed, they are given ts-remove class, so you can transition to that.

Original idea is from idiomorph. TwinSpark has a somewhat smaller implementation, see code for details (search for Morph).


Multiple Children #

If you look how endless scrolling is implemented in HTML, it's usually a long list of elements inside some other element - so you have to deal with several elements being appended to a parent. For this and similar use cases there is a modifier children in ts-req-selector.


Element 1 Element 2

<p class="list2">
  <span class="chip">Element 1</span>
  <span class="chip">Element 2</span>
  <button class="btn"
          ts-req-selector="children .list"
          ts-target="sibling .chip:last-of-type"
          ts-swap="morph">Morph last</button>

Dynamic Form Validation (advanced)#

Form validation is a common task, and TwinSpark allows to consolidate validation logic on the server. Surprisingly, it could be difficult, but ts-swap="morph" strategy allows us to just return whole new form with errors and not mess up with focus.

Important bits


Solve this problem with odd numbers

= 14

Animated Pagination #

Morphing gives you ability to add animations and transitions during HTML replacement. This example uses zero lines of custom JS, just some styles to add transitions.


Morphing Video #

This is a hard problem to solve with other algorithms (notably, morphdom gets it wrong). HTML structure is changed a lot, and element with an id is deep (enough) inside that structure, but Youtube video still plays after change.