sprinkle-js

A quick drop in reactive library to sprinkle reactivity in your web-app

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
sprinkle-js
700.4.65 months ago10 months agoMinified + gzip package size for sprinkle-js in KB

Readme

Logo MIT License npm bundle size npm npm GitHub last commit
Sprinkle JS
Sprinkle JS is the open source alternative to...well nothing! Joking aside, is a javascript library that let you sprinkle reactivity inside your vanilla imperative javascript. It uses a model very similar (yet far less powerful) than SolidJS to handle reactivity and it was heavily inspired by this post from Ryan Carniato the main developer behind it. Before diving in the APIs and all the cool stuff it's useful to clarify what is Sprinkle JS philosophy and what Sprinkle JS is not.

Philosophy

Have you ever been fiddling around in Codepen or in a local html file and wondered "Man I wish I could have a bit of reactivity for this stupid app i'm messing with". Well Sprinkle JS is exactly what you need! The main philosophy behind is to build a supersmall library that requires no bundler that you can just drop in a script tag or import from a cdn and add a bit a reactivity to your app. We made a bunch of utility functions to bind your variables to your DOM in some way and that's all is needed. Open a new file, drop in Sprinkle JS, declare your variables, declare your bindings and than proceed to write your code without caring about updating the DOM...that's Sprinkle JS work!

What is not

  • Sprinkle JS is not the next big thing after React.
  • Sprinkle JS is not for your big project...i mean if you want to use it
feel free to do so but it's not what's meant to be.
  • Sprinkle JS is not for perf aficionados: we as a community are trying to
our best and maybe it will become the best version of itself but keep always keep in mind the main philosophy behind it.## Installation To install Sprinkle JS you can run ```bash npm i sprinkle-js ``` This will install the npm package in your project and will let you import the various exported methods with ```typescript import SprinkleJS from "sprinkle-js"; ```
Tip: it's better to actually import the naming exports to allow for tree
shaking 🌳
```typescript
import { createVariable } from "sprinkle-js";
```
Installing the library from npm will download a copy into your node_modules and will be shipped together with your application when you publish it. This will kind of negate the meaning of this library but you are free to use it like this if you prefer. To use the library for what is meant to be go on codepen or in a local html file and paste this in the JS tab: ```javascript import { createVariable } from "https://cdn.skypack.com/sprinkle-js"; ``` Sprinkle JS is available from all major cdn's
  • https://unpkg.com/sprinkle-js
  • https://cdn.jsdelivr.net/npm/sprinkle-js
  • https://cdn.skypack.com/sprinkle-js
```html
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sprinkle JS App</title>
<!-- This will load the library in a SprinkleJS global variable -->
<script src="https://unpkg.com/sprinkle-js/dist/sprinkle-js.iife.js"></script>
<!-- You can reference that variable and destructure from it in
your script tag or even in your js file-->
<script>
const { createVariable, createEffect } = SprinkleJS;
</script>
``` If you append dist/sprinkle-js.iife.js to each of those links you can also embed it in a script tag.

On Codepen

The easiest way to get started with Sprinkle JS is just by going on Codepen by clicking this. This will bring you to codepen.io with the Sprinkle JS template.

On Stackblitz

You can quickly initialize a Stackblitz project setup with typescript and Sprinkle JS by going to this template.
Tip: we try to keep this templates up to date but it's safer to always
update the dependencies as soon as you fork it.

On Codesandbox

You can quickly initialize a Code Sandbox project setup with typescript and Sprinkle JS by going to this template.
Tip: we try to keep this templates up to date but it's safer to always
update the dependencies as soon as you fork it.

Demo

You can see this library in use here.

Authors

Contributing

Any contribution is welcomed, you can either open a New Issue or read the list of the open ones to work on them. A Contributing guide will be up ASAP. //⚠ WIP

Documentation

You can check the docs here. If you want to contribute to the docs website you can file an issue to this repository.

FAQ

Can i use Sprinkle JS in production?

Obviously you can...but i don't recommend it. The main focus of Sprinkle JS is not to give you a fully fledget, bleeding edge, blazingly fast framework. Is mainly to let you fiddle around in your fun little projects without the need to setup a bundler or importing a huge codebase just to get a bit of reactivity.

How can i create a component in Sprinkle JS?

You can't. You could, in theory write a function that returns an array of child nodes but components in the strict term are not part of the Sprinkle JS philosophy. We want to mantain a super small bundle size.

Why i can't create a variable that is not an Object?

Sprinkle uses javascript Proxyes to handle reactivity and unfortunately you can't create a Proxy from a primitive value.

Why i need to wrap everything in a function to use Sprinkle JS?

Sprinkle JS uses the same model of Vue or Solid to handle the reactivity. To keep track of the dependencies of a function it saves that function in a stack before calling it. This allows every variable accessed in that function to know that it's used in that function. This has some drawbacks. If you don't wrap everything inside a function the variable will be accessed before the effect can save himself into the stack. If you want to understand this better you can check this article from Ryan Carniato or watch this video from VueMastery on Vue reactivity.

Why my createEffect does not re-run? I've used a reactive variable inside it.

It could be a lot of different things but probably is because you are running asynchronous code inside of it. You can run asynchronous code inside a createEffect but for it to track your dependencies you have to make sure to use them before the async part. You can even just access it just by writing variablename.fieldname; and it will be correctly tracked.

Usage/Examples

setup

Given that Sprinkle JS uses the two core methods to do everything with this method you can plug your own reactivity system inside Sprinkle JS. Why should you do this? I don't know! For fun, or maybe because you already have @vue/reactivity in use in your application and you want to use it with Sprinkle JS methods. To plug your own reactivity system into Sprinkle JS you need to provide a method to createVariable and one for createEffect. Optionally you can provide an override for createComputed. This will override the core functionalities and the reactivity system of Sprinkle JS and every other method will use your reactivity system instead. Obviously there are constraints:
  • createEffect needs to automatically check its own dependencies
  • ideally you want to return an object or a Proxy from createVariable
  • if you don't return a Proxy from createVariable you'll probably need to
provide also a createComputed override @vue/reactivity provides a very similar set of core functionalities to Sprinkle JS so it's very easy to plug it into it. ```typescript import { effect, reactive } from "@vue/reactivity"; import { setup } from "sprinkle-js"; setup({ createVariable: reactive, createEffect: effect, }); ``` And that's it...from this moment on all the methods of Sprinkle JS will use @vue/reactivity as their reactivity system.
Warning while pretty similar the reactivity system of @vue/reactivity it's
a bit different so you might found some differences in behavior for some of
your applications.
The setup function returns a function to reset the setup. ```typescript import { effect, reactive } from "@vue/reactivity"; import { createEffect, createVariable, setup } from "sprinkle-js"; const reset = setup({ createVariable: reactive, createEffect: effect, }); //this variable uses @vue/reactivity const vueReactivityVar = createVariable({ whosCool: "you" }); createEffect(() => { console.log(vueReactivityVar.whosCool); }); reset(); //this variable uses sprinkle-js const sprinkleVar = createVariable({ whosCool: "you" }); createEffect(() => { console.log(sprinkleVar.whosCool); }); ```
N.B. once created inside a reactivity system the variable will continue to
use that reactivity system even after the reset.
While returning an object or a Proxy from createVariable is preferable it's not required. This means that if you want to use signals from solid-js you can do something like this ```typescript import { createEffect as solidEffect, createSignal } from "solid-js"; import { bindTextContent, createEffect, createVariable, setup, } from "sprinkle-js"; setup({ createVariable: (...props) => {
return createSignal(...props);
}, createEffect: (...props) => {
solidEffect(...props);
}, }); //note that the limit of passing an object to createVariable it's not present since we are using solid //createSignal under the hood, also the returned value it's not a variable but an array with an //accessor and a setter const state, setState = createVariable(1); createEffect(() => { //here we access the state like we would do in solid console.log(state()); }); //we need to use the same access method also inside the methods of Sprinkle JS bindTextContent("#app", () => The content of the state is ${state()}); ```

createRef

This method is used to create a reactive variable for a primitive. It wraps the primitive in an object with a value property. ```typescript const ref = createRef(1); console.log(ref.value); // 1 ``` if you use this variable inside a createEffect or inside another method whenever you'll update the value the method will re-run. You can also pass an equality function that will determine how to check for equality for this ref. By default it will use Object.is. ```typescript const ref = createRef(1, (before, after) => before > after); console.log(ref.value); // 1 ``` The above ref will not trigger an effect re-run if the previous value is greater than the new value. Please note: if you are using typescript you can't pass something different than a primitive value to the ref and if you pass an equality function is recommended to also use the generic version of the fucntion createRef<number>(1, (before, after)=> before > after) otherwise Typescript will narrow the type to a constant. If you are using this in Javascript you are allowed to pass object to the ref (although is not recommended since you'll have to access it unnecessarly via the .value property), the object passed will be deeply reactive.

createVariable

This method is used to create a reactive variable for an object. It'll throw if you try to pass a primitive value to it. ```typescript const variable = createVariable({ whosCool: "you" }); console.log(variable.whosCool); // you ``` if you use this variable inside a createEffect or inside another method whenever you'll update the value the method will re-run. You can also pass an object containing an equality function for every field of the object that will determine how to check for equality for that field. By default it will use Object.is. ```typescript const variable = createVariable({ whosCool: "you" }, { whosCool: (before, after) => before.length === after.length, }); console.log(variable.whosCool); // you ``` The above variable will not trigger an effect re-run if the previous value has the same length as the new value. createVariable works with nested property too. You can pass object inside objects and updating a values inside those object will trigger the rerun of the effects where it's been used. To pass an equality function relative to a nested object you should pass an object containing the properties of the object you want a particular equality function. If you pass a built in object tho (like Set, Map or HTMLElement) it will not become reactive. If you use a nested object you can pass an object with the same structure to the equality functions object to specify an equality function for every field. If you pass a function the equality function will not be applied to the nested object. ```typescript //here the name field will use the custom equality function const variable = createVariable({ whosCool: { name: "you" } }, { whosCool: { name: (before, after) => before.length === after.length }, }); //here the equality function will be applied only to the whosCool object const variable = createVariable({ whosCool: { name: "you" } }, { whosCool: (before, after) => before.length === after.length, }); ``` ```typescript const variable = createVariable({ whosCool: { pronoun: "you" } }, { whosCool: { pronoun: (before, after) => before.length === after.length }, }); console.log(variable.whosCool.pronoun); // you ``` in the above example variable.whosCool.pronoun is still reactive.

createCssVariable

This method is used to create a reactive variable for an object and map every field to a css variable. It'll throw if you try to pass a primitive value to it. In typescript you can pass only string or numbers as values of the object but every value will be stringified before assigning it to the css variable so if you pass a complex object you'll get "object Object" instead. ```typescript //this will create two css variable at the root level --x and --y with the values of 0 and 0 //you can use those variable inside your css const variable = createCssVariable({ x: 0, y: 0 }); //setting this will also set the --x css variable to 200 variable.x = 200; ``` if you use this variable inside a createEffect or inside another method whenever you'll update the value the method will re-run. You can also pass an object containing an equality function for every field of the object that will determine how to check for equality for that field. By default it will use Object.is. ```typescript const variable = createCssVariable({ whosCool: "you" }, { whosCool: (before, after) => before.length === after.length, }); ``` The above variable will not trigger an effect re-run if the previous value has the same length as the new value. A third argument of this method is a selector or an HTMLElement of the element you want to apply the css variables to. It default to :root. If the provided selector does not select anything it will apply the css variable to the :root

createStored

This method is used to create a reactive variable for an object also persisting it in localStorage or sessionStorage. It'll throw if you try to pass a primitive value to it. It will also automatically add a listener for the storage to update the variable whenever the storage changes. It will take a key and an initial value as input but will discard the initial value if the key is already present in the storage. It will also throw if the object in the storage is not Object-like ```typescript const variable = createStored("cool-stored", { whosCool: "you" }); console.log(variable.whosCool); // you console.log(window.localStorage.getItem("cool-stored")); // '{ "whosCool": "you" }' ``` if you use this variable inside a createEffect or inside another method whenever you'll update the value the method will re-run. You can also pass an object containing an equality function for every field of the object that will determine how to check for equality for that field. By default it will use Object.is. ```typescript const variable = createStored("cool-stored", { whosCool: "you" }, { whosCool: (before, after) => before.length === after.length, }); console.log(variable.whosCool); // you ``` The above variable will not trigger an effect re-run if the previous value has the same length as the new value. Differently from createVariable a stored object is not deeply reactive.

createComputed

This method is used to create a reactive computed variable. You need to pass a function that will return a value. The return value of the function will be inside of the .value field of the returned computed. If you use some other variable to compute the this value it will always be in sync. ```typescript const variable = createVariable({ whosCool: "you" }); const computed = createComputed(() => ${variable.whosCool} is cool!); console.log(computed.value); // you is cool! variable.whosCool = "whoever uses SprinkleJS"; console.log(computed.value); // whoever uses SprinkleJS is cool! ``` if you use this variable inside a createEffect or inside another method it will be rerunned whenever this computed changes. You can't set the value of a computed value and trying would still result in the same value being inside that computed. You can also pass an equality function that will determine how to check for equality for this computed. By default it will use Object.is. ```typescript const variable = createVariable({ whosCool: "you" }); const computed = createComputed( () => ${variable.whosCool} is cool!, (before, after) => before.length === after.length, ); console.log(computed.value); // you is cool! variable.whosCool = "whoever uses SprinkleJS"; console.log(computed.value); // whoever uses SprinkleJS is cool! ``` The above ref will not trigger an effect re-run if the previous value has the same length as the new value. Another simple way to create a computed value if by defining a function that return a value using a reactive variable like this ```typescript const variable = createVariable({ whosCool: "you" }); const computed = () => ${variable.whosCool} is cool!; console.log(computed()); // you is cool! variable.whosCool = "whoever uses SprinkleJS"; console.log(computed()); // whoever uses SprinkleJS is cool! ``` As you can see in the example, this require you to call the function to access the value (instead of accessing it by .value) However this second method will rerun the effect it's used in even if it has the same value as before.

createEffect

This method is used to create an effect that will keep track of it's dependencies and re-run every time they changed ```typescript const variable = createVariable({ whosCool: "you" }); const ref = createRef(1); createEffect(() => { console.log(variable.whosCool, ref.value); }); //will log (you, 1) the first time ref.value++; //the effect run again logging (you, 2) variable.whosCool = "whoever uses Sprinkle JS"; //the effect run again logging (whoever uses Sprinkle JS, 2) ``` You can also return a function that will be run before the new function to clean up the previous effect. ```typescript const variable = createVariable({ whosCool: "you" }); const ref = createRef(1); createEffect(() => { console.log(variable.whosCool, ref.value); return () => {
console.log("cleaning up");
}; }); //will log (you, 1) the first time ref.value++; //the effect run again logging first "cleaning up" and then (you, 2) variable.whosCool = "whoever uses Sprinkle JS"; //the effect run again logging first "cleaning up" and then (whoever uses Sprinkle JS, 2) ```

untrack

This function can be used inside a createEffect to untrack a dependency. This way you can use a reactive variable inside a create effect without triggering the re-run when that variable changes. It takes a function as input and it return anything returned from that function. ```typescript const variable = createVariable({ whosCool: "you" }); const ref = createRef(1); createEffect(() => { const refValue = untrack(() => ref.value); console.log(variable.whosCool, refValue); }); //will log (you, 1) the first time ref.value++; //the effect will not run again because ref.value has been accessed inside the untrack variable.whosCool = "whoever uses Sprinkle JS"; //the effect run again logging (whoever uses Sprinkle JS, 2) ```

batch

This function can be used to avoid running effects multiple times when changing multiple variables. It's as simple as calling batch and passing a function that will change some variables. ```typescript const variable = createVariable({ name: "John", lastName: "Doe" }); createEffect(() => { console.log(The full name is ${variable.name} ${variable.lastName}); }); //the effect will still run the first time logging "The full name is John Doe" batch(() => { variable.name = "Albert"; variable.lastName = "Einstein"; }); //changin both variables without the batch would've run the effect twice //in this case the effect will run a single time loggin "The full name is Albert Einstein" ```

bindTextContent

This function is used to bind a string value to the text content of an element. It takes a dom element or a selector as the first argument and a function returning the value to bind to the text content as the second argument. ```typescript const variable = createVariable({ whosCool: "you" }); const ref = createRef(1); bindTextContent("#div-to-bind", () => ${ref.value} ${variable.whosCool}); //the text content of the div with the id div-to-bind will be "1 you" ref.value++; //the text content of the div with the id div-to-bind will be "2 you" variable.whosCool = "whoever uses Sprinkle JS"; //the text content of the div with the id div-to-bind will be "2 whoever uses Sprinkle JS" ``` The callback you pass in also takes the element as the first argument, in Typescript you can pass a generic type specifying what kind of element you are expecting ```typescript const variable = createVariable({ whosCool: "you" }); const ref = createRef(1); bindTextContent( "#div-to-bind", (element: HTMLDivElement) =>
`${element?.textContent} ${ref.value} ${variable.whosCool}`,
); //the text content of the div with the id div-to-bind will be "1 you" ref.value++; //the text content of the div with the id div-to-bind will be "1 you 2 you" variable.whosCool = "whoever uses Sprinkle JS"; //the text content of the div with the id div-to-bind will be "1 you 2 you 2 whoever uses Sprinkle JS" ``` If you need to have access to the selected element (to add event listeners for example), the element is returned from the function. ```typescript //this will bind the variables to the textContent and you'll have access to the element itself inside the variable divToBind const divToBind = bindTextContent( "#div-to-bind", (element: HTMLDivElement) =>
`${element?.textContent} ${ref.value} ${variable.whosCool}`,
); ```

bindInnerHTML

Warning Sprinkle JS does not sanitize the content of the innerHTML. If you
use this function with user input make sure to sanitize it first to avoid
expose yourself to XSS attacks.
This function is used to bind a string value to the innerHTML of an element. It takes a dom element or a selector as the first argument and a function returning the value to bind to the innerHTML as the second argument. ```typescript const variable = createVariable({ whosCool: "you" }); const ref = createRef(1); bindInnerHTML( "#div-to-bind", () => <span>${ref.value} <strong>${variable.whosCool}</strong></span>, ); //the innerhtml of the div with the id div-to-bind will be "1 you" ref.value++; //the innerhtml of the div with the id div-to-bind will be "2 you" variable.whosCool = "whoever uses Sprinkle JS"; //the innerhtml of the div with the id div-to-bind will be "2 whoever uses Sprinkle JS" ``` The callback you pass in also takes the element as the first argument, in Typescript you can pass a generic type specifying what kind of element you are expecting ```typescript const variable = createVariable({ whosCool: "you" }); const ref = createRef(1); bindInnerHTML( "#div-to-bind", (element: HTMLDivElement) =>
`<span>${element?.textContent} ${ref.value} <strong>${variable.whosCool}</strong></span>`,
); //the innerhtml of the div with the id div-to-bind will be "1 you" ref.value++; //the innerhtml of the div with the id div-to-bind will be "1 you 2 you" variable.whosCool = "whoever uses Sprinkle JS"; //the innerhtml of the div with the id div-to-bind will be "1 you 2 you 2 whoever uses Sprinkle JS" ``` If you need to have access to the selected element (to add event listeners for example), the element is returned from the function. ```typescript //this will bind the variables to the textContent and you'll have access to the element itself inside the variable divToBind const divToBind = bindInnerHTML( "#div-to-bind", (element: HTMLDivElement) =>
`${element?.textContent} ${ref.value} ${variable.whosCool}`,
); ```

bindInputValue

This function is used to bind a string value to the value of an input element. It takes an input dom element or a selector as the first argument and a function returning the value to bind to the input value as the second argument. The following code will bind the input value to the variable.whosCool field. Note that is a one-way binding so make sure to also add an event listener on the input to complete the flow. We are saving the return value of the function into bindInputValue to later add the event listener to it. ```typescript const variable = createVariable({ whosCool: "you" }); const inputToBind = bindInputValue("#input-to-bind", () => variable.whosCool); inputToBind.addEventListener("input", (e) => { variable.whosCool = e.target.value; }); ``` The callback you pass in also takes the element as the first argument. ```typescript const variable = createVariable({ whosCool: "you" }); const inputToBind = bindInputValue( "#input-to-bind", (element) => element.innerText + " " + variable.whosCool, ); inputToBind.addEventListener("input", (e) => { variable.whosCool = e.target.value; }); ```

bindDom

This function is used to bind an object that describe some DOM properties to the actual DOM properties. It takes a dom element or a selector as the first argument and a function returning the object as the second argument. The following code will bind the ariaLabel value to the variable.whosCool field and the checked value for the checkbox. ```typescript const variable = createVariable({ whosCool: "you" }); bindDom("#checkbox", (element) => ({ ariaLabel: variable.whosCool, checked: variable.whosCool === "you", })); ```

bindClass

This function is used to bind a class to the actual element. It takes a dom element or a selector as the first argument, the class that you want to apply and a function returning a boolean as the third argument. ```typescript const variable = createVariable({ count: 0 }); bindClass("#div-to-bind", "dark", (element) => variable.count % 2 === 0); //the div will have the dark class set by default //this will remove the class; variable.count++; //this will add the class again; variable.count++; ```

bindClasses

This function is used to bind multiple classes to the actual element. It takes a dom element or a selector as the first argument, and an object containing the classes that you want to apply as the keys and a boolean as the value. Each class will be applied only if the corrispondent value is true. ```typescript const variable = createVariable({ count: 1 }); bindClasses("#to-bind", () => ({ one: variable.count === 1, two: variable.count === 2, three: variable.count === 3, lessThanFive: variable.count < 5, })); //the div will have the classes one and lessThanFive //the div will have the classes two and lessThanFive variable.count++; //the div will have the classes three and lessThanFive variable.count++; //the div will have the class lessThanFive variable.count++; //the div will have no classes variable.count++; ```

bindStyle

This function is used to bind an object that describe the style of an element to the actual element style. It takes a dom element or a selector as the first argument and a function returning the object as the second argument. The following code will bind color property and the backgroundColor property value to the variable.color and variable.bg field. ```typescript const variable = createVariable({ color: "black", bg: "#BADA55" }); bindStyle("#div-to-bind", (element) => ({ color: variable.color, backgroundColor: variable.bg, })); variable.bg = "#C0FFEE"; //the div will now have #C0FFEE as backgroundColor variable.color = "white"; //the div will now have white as the color ```

bindChildren

This function is used to bind html as the children of an element. How is this different than bindInnerHTML? Each item can have a key attribute and if the key attribute does not change the element will not change either (it will have the same reference in the dom). It takes a dom element or a selector as the first argument, a function returning a document fragment or a string or an array of document fragments of an array of strings as the second argument and an option function to run after the diffing as the third parameter. The third argument can be useful to bind something to the newly created element. It takes the root element and a Map object as parameter where the keys are all the keys you've specified in the template and the values are the element associated with that key that is on the DOM. Every element has an isNew flag that specifies if it's a newly added element or an old one Given that sometimes is difficult to build a document fragment from scratch Sprinkle JS an html tagged template to transform your string into an actual document fragment. Inside this tagged template literal is possible to include events with the syntax on:eventname and there's a special event on:bind to run code whenever the element will be binded to the DOM by sprinkle JS. This event will get the actual element as the parameter and it's possible to use this to call other Sprinkle JS methods on that specific element. For example: ```typescript const variable = createVariable({inputVal: ""}); bindChildren("#div-to-bind", ()=> html`
<input
on:bind=${(inputElement)=>{
bindInputValue(inputElement, ()=> variable.inputVal)
}}
on:input=${(e)=>{
variable.inputVal= e.target.value;
}} />
`); ``` will bound to the div #div-to-bind an input as children and will bind the inputValue of it variable.inputVal.
Tip: this is a better way than the afterRun function that bindChildren takes as a third argoument to bind something to an element.
Every array inside the tagged template literal will be treated as separate elements. So for example ```typescript html${[1,2,3,4].map(num => html ${num} )} ``` will return a fragment with 4 buttons. As you've seen from this previous example one small caveat of this function is that you have to repeat the tag at each new "level" (you can skip the first one since everything you return will still get passed through the html function automatically but you have to include it inside the map function) but this allow for enaugh composability that you can actually start to create some sort of components. A component it's simply a function that return the return value of an html tagged template literal. The above example could be rewritten like so ```typescript const MagicButton = ({num})=>{ return html<button>${num}</button> } html${[1,2,3,4].map(num => MagicButton({num}))} ``` Those are all small examples so here's a fully featured one ```typescript const variable = createVariable({ listOfCoolThings:
"you",
"sprinkle-js",
"javascript",
, }); bindChildren( "#ul-to-bind", (element) => variable.listOfCoolThings.map((coolThing) => html`
<li key="${coolThing}">
<button 
key="${coolThing}-button"
on:click=${()=>{
console.log(`You've pressed the ${coolThing} button`);
}}
>Log ${coolThing}</button>
</li>`
),
(element, elements) => {
const youElement = elements.get("you");
//yeah i know this is a silly example
bindClasses(youElement, ()=>({
"classForYou": true,
});
}, ); variable.listOfCoolThings = ...variable.listOfCoolThings, "npm"; //this will add a new li element to the ul ``` A small caveat is that given that every nested element get's diffed you should add a key to every element that you want to preserve the reference to.
Warning bindChildren changed api's over time so make sure you are on the latest version o refer to previous versions of this readme for the old documentation.

Running Tests

To run tests, run the following command ```bash npm run test ```