{"id":20513883,"url":"https://github.com/webreflection/uce-template","last_synced_at":"2025-09-12T23:31:13.256Z","repository":{"id":54144343,"uuid":"297601339","full_name":"WebReflection/uce-template","owner":"WebReflection","description":"A Vue 3 inspired Custom Elements toolless alternative.","archived":false,"fork":false,"pushed_at":"2021-07-02T08:02:47.000Z","size":1176,"stargazers_count":108,"open_issues_count":3,"forks_count":7,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-12-31T15:32:58.260Z","etag":null,"topics":["components","components-and-templates","web","web-components"],"latest_commit_sha":null,"homepage":"https://medium.com/@WebReflection/a-new-web-components-wonder-f9e042785a91","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WebReflection.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-22T09:32:44.000Z","updated_at":"2024-07-06T15:32:50.000Z","dependencies_parsed_at":"2022-08-13T07:31:12.224Z","dependency_job_id":null,"html_url":"https://github.com/WebReflection/uce-template","commit_stats":null,"previous_names":[],"tags_count":76,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fuce-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fuce-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fuce-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fuce-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WebReflection","download_url":"https://codeload.github.com/WebReflection/uce-template/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":232798442,"owners_count":18578096,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["components","components-and-templates","web","web-components"],"created_at":"2024-11-15T21:13:44.324Z","updated_at":"2025-01-06T23:25:04.095Z","avatar_url":"https://github.com/WebReflection.png","language":"JavaScript","readme":"# \u003cem\u003eµ\u003c/em\u003ece-template\n\n[![Downloads](https://img.shields.io/npm/dm/uce-template.svg)](https://www.npmjs.com/package/uce-template) [![CSP friendly](https://webreflection.github.io/csp/friendly.svg)](https://webreflection.github.io/csp/#-csp-friendly)\n\n![winter sky](./test/uce-template-head.jpg)\n\n\u003csup\u003e**Social Media Photo by [Federico Bottos](https://unsplash.com/@landscapeplaces) on [Unsplash](https://unsplash.com/)**\u003c/sup\u003e\n\nA tiny toolless library with tools included. **[Live demo](https://webreflection.github.io/uce-template/test/)**\n\n- - -\n\n### 📣 Community Announcement\n\nPlease ask questions in the [dedicated discussions repository](https://github.com/WebReflection/discussions), to help the community around this project grow ♥\n\n---\n\nInspired by [Vue 3 \"_One Piece_\"](https://github.com/vuejs/vue-next/releases/tag/v3.0.0), _uce-template_ provides a custom builtin `\u003ctemplate\u003e` element to define components in a _Vue_ fashion.\n\n```html\n\u003ctemplate is=\"uce-template\"\u003e\n\n  \u003cstyle scoped\u003e\n  span { color: green }\n  \u003c/style\u003e\n\n  \u003cthe-green\u003e\n    The \u003cspan\u003e{{thing}}\u003c/span\u003e is green\n  \u003c/the-green\u003e\n\n  \u003cscript type=\"module\"\u003e\n  export default {\n    setup() {\n      return {thing: 'world'}\n    }\n  }\n  \u003c/script\u003e\n\n\u003c/template\u003e\n```\n\nAdd this library to the equation, and [see it bootstrapping](https://codepen.io/WebReflection/pen/xxVMgZx?editors=1000) all defined components.\n\n- - -\n\n## Getting Started\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eFeatures\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\n  * **SSR** compatibility out of the box: components definitions land *once* so no duplicated templates are needed in both layout and *JS*\n  * a simple **CLI** that converts any html page or component into its minified version and, optionally, *Babel* transpilation\n  * **Custom Elements** based, including builtin extends, so that *IE11*, *Safari*, or any other browser, will work right away\n  * optionally **lazy** `\u003ctemplate lazy\u003e` component, to resolve their definition only when live\n  * optionally **shadow**ed `\u003ccustom-element shadow\u003e` components, and optionally shadowed `\u003cstyle shadow\u003e` styles\n  * a variety of pre-defined modules to import, including a virtual `@uce` module, to create reactive *UIs* and more\n  * a runtime *ESM -\u003e CommonJS* **module** system, where relative dependencies are [resolved (once) lazily](./extra-details.md#the-lazy-js-environment), but any imported [module can be pre-defined](./extra-details.md#the-module-js-environment) through the `resolve(name, module)` exported utility\n  * everything pre-bundled fits into *10K* gzipped budget, or *9K* via brotli, but it's only *7K* gzip, and *6.5K* brotli in its *no-polyfills* version 🦄\n\n#### Goals\n\n  * demonstrate that tools and tooling can be optional, thanks to the current state of the *Web*\n  * avoid any debate regarding duplicated code and re-hydration cost: each component can be served through static pages or dynamic *SSR*, without needing duplicated code around\n  * being ahead of time providing the long discussed partial templates already, improving the previous *HTML Imports* idea, which has been dropped anyway, and simplifying scoped styles via auto prefixes or shadow dom\n  * being extremely developer friendly with a script anyone can add on any page to start with, with the optional tooling offered by the module itself to optimize stand alone components, or even whole *HTML* pages\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eCLI\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nWhile it's suggested to install the *CLI* globally, due some not-super-light dependency, it's still an `npx` command away:\n\n```sh\n# check all options and usage\nnpx uce-template --help\n\n# works with files\nnpx uce-template my-component.html\n\n# works with stdin\ncat my-component.html | uce-template\n```\n\nThat's it, but of course we should be sure that produced layout still works as expected 👍\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003e\u0026lt;template\u0026gt;\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nAny template that extends `uce-template` *must* contain at least a custom element in it, either regular, or built-in extend:\n\n```html\n\u003c!-- register regular-element --\u003e\n\u003ctemplate is=\"uce-template\"\u003e\n  \u003cregular-element\u003e\n    regular\n  \u003c/regular-element\u003e\n\u003c/template\u003e\n\n\u003c!-- register builtin-element as div --\u003e\n\u003ctemplate is=\"uce-template\"\u003e\n  \u003cdiv is=\"builtin-element\"\u003e\n    builtin\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\nAny template *might* contain a single `\u003cscript\u003e` tag, and/or one or more `\u003cstyle\u003e` definitions.\n\n\n#### \u0026lt;slot\u0026gt;\n\nIf a component contains `{{slot.name}}` definitions, nodes from the living *HTML*, before the component gets upgraded, will be placed in there once live.\n\nSee this [live example](https://codepen.io/WebReflection/pen/OJNdZPB?editors=1000) to understand more.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003e\u0026lt;custom-element\u0026gt;\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nEach \"*component*\" might define itself with, or without, its own static, or dynamic, content.\n\nSuch *content* will be used to render each custom element once \"*mounted*\" (live) and per each reactive state change, but *only* if the template is not an empty one.\n\nAll **dynamic parts** must be wrapped within `{{dynamic}}` curly brackets as shown here:\n\n```html\n\u003cmy-counter\u003e\n  \u003cbutton onclick={{dec}}\u003e - \u003c/button\u003e\n  \u003cspan\u003e{{state.count}}\u003c/span\u003e\n  \u003cbutton onclick={{inc}}\u003e + \u003c/button\u003e\n\u003c/my-counter\u003e\n```\n\nThe `state`, `dec`, and `inc` references will be passed along through the script node, if any.\n\nWhenever the component is rendered, its update callback is invoked providing the element itself as a **context**.\n\n```html\n\u003cbutton is=\"my-button\"\u003e\n  I am a {{this.tagName}}\n\u003c/button\u003e\n```\n\nRegarding **ShadowDOM**, its polyfill is not included in this project but it's possible to define a component through its *shadow root* by adding a *shadow* attribute:\n\n```html\n\u003cmy-counter shadow\u003e\n  \u003c!-- this content will be in the shadowRoot --\u003e\n  \u003cbutton onclick={{dec}}\u003e - \u003c/button\u003e\n  \u003cspan\u003e{{state.count}}\u003c/span\u003e\n  \u003cbutton onclick={{inc}}\u003e + \u003c/button\u003e\n\u003c/my-counter\u003e\n```\n\nThe `shadow` attribute is `open` by default, but it can also be specified as `shadow=closed`.\n\nRegarding `{{JS}}`, if attribute, and you'd like to use `{{ JS }}` spaces around, the attribute *must* be in quotes, otherwise the *HTML* template breaks the layout in unexpected ways.\n\n```html\n\u003c!-- OK --\u003e\n\u003cmy-counter\u003e\n  \u003cbutton onClick={{dec}}\u003e - \u003c/button\u003e\n\u003c/my-counter\u003e\n\n\u003c!-- OK --\u003e\n\u003cmy-counter\u003e\n  \u003cbutton onClick=\"{{ dec }}\"\u003e - \u003c/button\u003e\n\u003c/my-counter\u003e\n\n\u003c!-- IT BREAKS!!! --\u003e\n\u003cmy-counter\u003e\n  \u003cbutton onClick={{ dec }}\u003e - \u003c/button\u003e\n\u003c/my-counter\u003e\n```\n\n### The curious `\u003c!--{{interpolation}}--\u003e` case\n\nAs everything in here is mostly based on standard *HTML* behavior, there are cases where an interpolation should be wrapped as comment.\n\nThe rule of thumb is that if you don't see the layout, or you read some *Bad template* error, it is possible that your interpolation could've been swallowed by the *template* element.\n\nThis happens mostly with elements such as **table**, **select**, and other elements that accept only a specific type of child node, but not text.\n\n```html\n\u003c!-- 👎 this won't work as expected --\u003e\n\u003ctable is=\"my-table\"\u003e\n  \u003ctbody\u003e{{rows}}\u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- 👍 this works 🎉 --\u003e\n\u003ctable is=\"my-table\"\u003e\n  \u003ctbody\u003e\u003c!--{{rows}}--\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n```\n\nIn the first case, the `\u003ctbody\u003e` would ignore any node that is not a `\u003ctr\u003e` *except for comments*, because comments don't get swallowed, or lost, in the process.\n\nYou can see the [dbmonster.html](./test/dbmonster.html) file definition for both the custom `\u003ctable\u003e` and the custom `\u003ctr\u003e` component.\n\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003e\u0026lt;style\u0026gt;\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nA component can have *one or more* styles in it, within a specific *scope*:\n\n  * a generic `\u003cstyle\u003e` will apply its content globally, useful to address `my-counter + my-counter {...}` cases, as example\n  * a `\u003cstyle scoped\u003e` will apply its content prefixed with the Custom Element name (i.e. `my-counter span, my-counter button {...}`)\n  * a `\u003cstyle shadow\u003e` will apply its content on top of the *shadowRoot*, assuming the component is defined with a `shadow` attribute\n\nThere is nothing special to consider here, except that *global* styles might interfere with *IE11* if too obtrusive, as once again *IE11* doesn't understand the `\u003ctemplate\u003e` element purpose and behavior.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003e\u0026lt;script\u0026gt;\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nA definition can contain only *one script tag* in it, and such *script* will be virtually handled like a *module*.\n\nSince *IE11* is *not* compatible with `\u003ctemplate\u003e` elements, if the `type` is not specified, *IE11* will try to evaluate all scripts on the page right-away.\n\nAccordingly, the `type` attribute can really have any value, as it's completely irrelevant for this library, but such value must not be IE11 compatible, and `module` is just one value that *IE11* would ignore.\n\nThe script *might* contain a `default export`, or even a `module.exports = ...`, where such export *might* have a `setup(element) { ... }` method that returns what the *dynamic* parts of the component expect:\n\n```html\n\u003cscript type=\"module\"\u003e\nimport {reactive} from '@uce';\nexport default {\n  setup(element) {\n    const state = reactive({ count: 0 });\n    const inc = () =\u003e { state.count++ };\n    const dec = () =\u003e { state.count-- };\n    return {state, inc, dec};\n  }\n};\n\u003c/script\u003e\n```\n\nThe `@uce` *reactive* helper makes it possible to automatically update the view whenever one of its properties changes.\n\nTo know more about reactive changes, please [read this Medium post](https://medium.com/@WebReflection/reactive-state-for-data-dom-78332ddafd0e).\n\n### The `setup` attribute\n\nIf a `\u003cscript type=\"module\" setup\u003e` is found, the content of the script is invoked with the element itself as context.\n\n[Live demo](https://webreflection.github.io/uce-template/test/setup.html)\n\n```html\n\u003cx-clock\u003e\u003c/x-clock\u003e\n\u003ctemplate is=\"uce-template\"\u003e\n  \u003cx-clock\u003e{{time}}\u003c/x-clock\u003e\n  \u003cscript type=\"module\" setup\u003e\n    let id = 0;\n    export default {\n      get time() {\n        return (new Date).toISOString();\n      }\n    };\n    this.connected = e =\u003e id = setInterval(this.render, 1000 / 30);\n    this.disconnected = e =\u003e clearInterval(id);\n  \u003c/script\u003e\n\u003c/template\u003e\n```\n\nThis shortcut is specially handy for components that don't need to setup *observedAttributes* but might need to setup *props*, and for the latter case, the `setup` attribute should contain `props`.\n\n```html\n\u003cscript type=\"module\" setup=\"props\"\u003e\n  // props are defined as key =\u003e defaultValue pairs\n  export const props = {\n    name: this.name || 'anonymous',\n    age: +this.age || 0\n  };\n\u003c/script\u003e\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n- - -\n\n## How to / Examples\n\nThis section goal is to showcase basic to complex examples via *uce-template*, where some example might use the `.uce` extension to confine components within their own files.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eView \u003ccode\u003e.uce\u003c/code\u003e files as HTML\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nIf you are using VS Code, you can *Ctrl+Shift+p*, type *settings JSON*, choose *Open Settings (JSON)*, and add the following to such file in order to highlight `.uce` files as *HTML*:\n\n```js\n{\n  \"other-settings\": \"...\",\n\n  \"files.associations\": {\n    \"*.uce\": \"html\"\n  }\n}\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eLazy Loaded Components\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nIf we define components as `view/my-component.uce` we might as well decide to include these lazily, or better, only when these are found in the current page.\n\nThis approach simplifies a lot bundles, dependencies, unnecessary bloat, and it can be done by including just `uce-template` and the tiny \u003csup\u003e\u003csub\u003e(364 bytes)\u003c/sub\u003e\u003c/sup\u003e [uce-loader](https://github.com/WebReflection/uce-loader#readme) as bootstrap, eventually defining extra dependencies used across components.\n\n```js\nimport {parse, resolve} from 'uce-template';\nimport loader from 'uce-loader';\n\n// optional components dependencies\nimport something from 'cool';\nresolve('cool', something);\n\n// bootstrap the loader\nloader({\n  on(component) {\n    // ignore uce-template itself\n    if (component !== 'uce-template')\n      fetch(`view/${component}.uce`)\n        .then(body =\u003e body.text())\n        .then(definition =\u003e {\n          document.body.appendChild(\n            parse(definition)\n          );\n        });\n  }\n});\n```\n\nThe same technique could be used directly on any *HTML* page, writing some code that might be compatible with *IE11* too.\n\n```html\n\u003c!doctype html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cscript defer src=\"//unpkg.com/uce-template\"\u003e\u003c/script\u003e\n    \u003cscript defer src=\"//unpkg.com/uce-loader\"\u003e\u003c/script\u003e\n    \u003cscript defer\u003e\n    addEventListener(\n      'DOMContentLoaded',\n      function () {\n        uceLoader({\n          Template: customElements.get('uce-template'),\n          on: function (name) {\n            if (name !== 'uce-template') {\n              var xhr = new XMLHttpRequest;\n              var Template = this.Template;\n              xhr.open('get', name + '.uce', true);\n              xhr.send(null);\n              xhr.onload = function () {\n                document.body.appendChild(\n                  Template.from(xhr.responseText)\n                );\n              };\n            }\n          }\n        });\n      },\n      {once: true}\n    );\n    \u003c/script\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cmy-component\u003e\n      \u003cp slot=\"content\"\u003e\n        Some content to show in \u003ccode\u003emy-component\u003c/code\u003e\n      \u003c/p\u003e\n    \u003c/my-component\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eLazy Loaded \u003ccode\u003euce-template\u003c/code\u003e\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nIf the majority of our pages don't use components at all, adding 7K+ of *JS* on top of each page might be undesired.\n\nHowever, we can follow the very same *Lazy Loaded Components* approach, except our loader will be in charge of bringing in also the *uce-template* library, either when an *uce-template* itself is found, or any other component.\n\n```js\nimport loader from 'uce-loader';\nloader({\n  on(component) {\n    // first component found, load uce-template\n    if (!this.q) {\n      this.q = [component];\n      const script = document.createElement('script');\n      script.src = '//unpkg.com/uce-template';\n      document.body.appendChild(script).onload = () =\u003e {\n        // get the uce-template class to use its .from(...)\n        this.Template = customElements.get('uce-template');\n        // load all queued components\n        for (var q = this.q.splice(0), i = 0; i \u003c q.length; i++)\n          this.on(q[i]);\n      };\n    }\n    // when uce-template is loaded\n    else if (this.Template) {\n      // ignore loading uce-template itself\n      if (component !== 'uce-template') {\n        // load the component on demand\n        fetch(`view/${component}.uce`)\n          .then(body =\u003e body.text())\n          .then(definition =\u003e {\n            document.body.appendChild(\n              this.Template.from(definition)\n            );\n          });\n      }\n    }\n    // if uce-template is not loaded yet\n    // add the component to the queue\n    else\n      this.q.push(component);\n  }\n});\n```\n\nUsing this technique, our *JS* payload per page would be now reduced to less than *0.5K* once above code gets bundled and minified, while everything else will happen automatically only if there are components somewhere in the page.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eLazy loaded expected components\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nAs the page could contain other custom elements from third party and libraries, it might be a good idea to predefine a well known *Set* of expected components, as opposite of trying to load any possible custom elements via the `view/${...}.uce` request.\n\nPrevious lazy loading techniques would work just fine already, but instead of checking that the component name is not `uce-template`, we could use a *Set*:\n\n```js\nloader({\n  known: new Set(['some-comp', 'some-other']),\n  on(component) {\n    if (this.known.has(component))\n      fetch(`view/${component}.uce`)\n        .then(body =\u003e body.text())\n        .then(definition =\u003e {\n          document.body.appendChild(\n            parse(definition)\n          );\n        });\n  }\n});\n```\n\nThe advantage of this technique is that the `known` *Set* could be dynamically generated through the list of `view/*.uce` files so that nothing would break if the found component is not part of the *uce-template* family.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eCSP \u0026amp; integrity/nonce\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\n`uce-template` inevitably needs to use `Function` to evaluate either [template partials](https://github.com/WebReflection/tag-params#caveats) or in-script *require(...)*.\n\nIt is recommended to increase security using either the __nonce__ `ijeLM8+5uwZ7ZXFmK+H2dwIWdiKJ1A4zhZIsq2Ffqqo=` or the *integrity* attribute, trusting via [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) only scripts that comes from our own domain.\n\n```html\n\u003cmeta http-equiv=\"Content-Security-Policy\" content=\"script-src 'self' 'unsafe-eval'\"\u003e\n\u003cscript defer src=\"/js/uce-template.js\"\n        integrity=\"sha256-ijeLM8+5uwZ7ZXFmK+H2dwIWdiKJ1A4zhZIsq2Ffqqo=\"\n        crossorigin=\"anonymous\"\u003e\n\u003c/script\u003e\n```\n\nPlease note that these values **change on every release** so please be sure you have the latest version (this README reflects the latest).\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eComponent own events\u003c/strong\u003e \u003csup\u003e\u003csub\u003e( without props )\u003c/sub\u003e\u003c/sup\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nAs it is for [uce](https://github.com/WebReflection/uce#readme), if the definition contains `onEvent(){...}` methods, these will be used to define the component.\n\nHowever, since states are usually decoupled from the component itself, it's a good idea to use a *WeakMap* to relate any component with its state and ... don't worry, *WeakMap* is natively supported in *IE11* too!\n\n[Live demo](https://codepen.io/WebReflection/pen/KKzERew?editors=1000)\n\n```html\n\u003cbutton is=\"my-btn\"\u003e\n  Clicked {{times}} times!\n\u003c/button\u003e\n\u003cscript type=\"module\"\u003e\n  const states = new WeakMap;\n  export default {\n    setup(element) {\n      const state = {times: 0};\n      states.set(element, state);\n      return state;\n    },\n    onClick() {\n      states.get(this).times++;\n      // update the current view if the\n      // state is not reactive\n      this.render();\n    }\n  };\n\u003c/script\u003e\n```\n\nPlease note this example covers any *state* VS *component* use case, as using the *WeakMap* is a recommendation.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eComponent own events\u003c/strong\u003e \u003csup\u003e\u003csub\u003e( with props )\u003c/sub\u003e\u003c/sup\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nIf `props` object is defined, and since *props** update the view automatically once changed, we might not need a *WeakMap* to relate the component's state.\n\n[Live demo](https://codepen.io/WebReflection/pen/XWdGqxp?editors=1000)\n\n```html\n\u003cbutton is=\"my-btn\"\u003e\u003c/button\u003e\n\u003ctemplate is=\"uce-template\"\u003e\n  \u003cbutton is=\"my-btn\"\u003e\n    Clicked {{this.times}} times!\n  \u003c/button\u003e\n  \u003cscript type=\"module\"\u003e\n    export default {\n      props: {times: 0},\n      onClick() {\n        this.times++;\n      }\n    };\n  \u003c/script\u003e\n\u003c/template\u003e\n```\n\nThe advantage of using props is that it's possible to define an initial state through attributes, or via direct setting it when rendered through the `html` utility, so that having a button with `times=\"3\"`, as example, would be rendered showing *Clicked 3 times!* right away.\n\n```html\n\u003cbutton is=\"my-btn\" times=\"3\"\u003e\u003c/button\u003e\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eMultiple refs\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThe `import {ref} from '@uce'` helper simplifies retrieval of node by `ref=\"name\"` attribute.\n\n```html\n\u003celement-details\u003e\n  \u003cspan ref=\"name\"\u003e\u003c/span\u003e\n  \u003cspan ref=\"description\"\u003e\u003c/span\u003e\n\u003c/element-details\u003e\n\n\u003ctemplate is=\"uce-template\"\u003e\n  \u003celement-details\u003e\u003c/element-details\u003e\n  \u003cscript type=\"module\" setup\u003e\n    import {ref} from '@uce';\n    const {name, description} = ref(this);\n    name.textContent = 'element name';\n    description.textContent = 'element description';\n  \u003c/script\u003e\n\u003c/template\u003e\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eMultiple, dynamic, slots\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThe `import {slot} from '@uce'` helper simplifies retrieval of slots by name, returning an *array* of elements grouped through the same name.\n\nThis can be used either to place single slots in interpolations, as [shown in this example](https://codepen.io/WebReflection/pen/OJNdZPB?editors=1000), or to place multiple slots within the same node.\n\n[Live demo](https://codepen.io/WebReflection/pen/NWNJVLR?editors=1000)\n\n```html\n\u003cfilter-list\u003e\n  Loading filter ...\n  \u003cul\u003e\n    \u003cli slot=\"list\"\u003esome\u003c/li\u003e\n    \u003cli slot=\"list\"\u003esearchable\u003c/li\u003e\n    \u003cli slot=\"list\"\u003etext\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/filter-list\u003e\n\n\u003ctemplate is=\"uce-template\"\u003e\n  \u003cfilter-list\u003e\n    \u003cdiv\u003e\n      \u003cinput placeholder=filter oninput={{filter}}\u003e\n    \u003c/div\u003e\n    \u003cul\u003e\n      {{list}}\n    \u003c/ul\u003e\n  \u003c/filter-list\u003e\n  \u003cscript type=\"module\"\u003e\n    import {slot} from '@uce';\n    export default {\n      setup(element) {\n        const list = slot(element).list || [];\n        return {\n          list,\n          filter({currentTarget: {value}}) {\n            for (const li of list)\n              li.style.display =\n                li.textContent.includes(value) ? null : 'none';\n          }\n        };\n      }\n    };\n  \u003c/script\u003e\n\u003c/template\u003e\n```\n\n**However**, in cases where the same-name slots order is not necessarily visualized sequentially, it is always possible to pass an array of nodes instead.\n\nThat is, any interpolation value can be a DOM node, some value, or an Array of nodes, same way [µhtml](https://github.com/WebReflection/uhtml#readme) works.\n\n[Live demo](https://codepen.io/WebReflection/pen/JjXzqww?editors=1000)\n\n```html\n\u003chowto-tabs\u003e\n  \u003cp\u003eLoading tabs ...\u003c/p\u003e\n  \u003chowto-tab role=\"heading\" slot=\"tab\"\u003eTab 1\u003c/howto-tab\u003e\n  \u003chowto-panel role=\"region\" slot=\"panel\"\u003eContent 1\u003c/howto-panel\u003e\n  \u003chowto-tab role=\"heading\" slot=\"tab\"\u003eTab 2\u003c/howto-tab\u003e\n  \u003chowto-panel role=\"region\" slot=\"panel\"\u003eContent 2\u003c/howto-panel\u003e\n\u003c/howto-tabs\u003e\n\n\u003ctemplate is=\"uce-template\"\u003e\n  \u003chowto-tabs\u003e\n    {{tabs}}\n  \u003c/howto-tabs\u003e\n  \u003cscript type=\"module\"\u003e\n    import {slot} from '@uce';\n    export default {\n      setup(element) {\n        const {tab, panel} = slot(element);\n        const tabs = tab.reduce(\n          (tabs, tab, i) =\u003e tabs.concat(tab, panel[i]),\n          []\n        );\n        return {tabs};\n      }\n    };\n  \u003c/script\u003e\n\u003c/template\u003e\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eProvide yor own modules / dependencies\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThe *module system* provided by *uce-template* is extremely simple and fully extendible, so that each component can `import any from 'thing';` as long as `thing` has been provided/resolved via the library.\n\n#### Resolve at build time\n\nIf we are going to define a single bundle entry point, and we know that each component would need one or more dependency, we can do the following:\n\n```js\nimport {resolve} from 'uce-template';\n\nimport moduleA from '3rd-party';\nconst moduleB = {any: 'value'};\n\nresolve('module-a', moduleA);\nresolve('module-b', moduleB);\n```\n\nOnce this build lands as single Web page entry point, all components would be able to *import* right away all base/default modules, plus all those pre-resolved.\n\n[Live demo](https://codepen.io/WebReflection/pen/XWdGByv?editors=1001) \u003csup\u003e\u003csub\u003e(see both HTML and JS panel + console)\u003c/sub\u003e\u003c/sup\u003e\n\n```html\n\u003cmy-comp\u003e\u003c/my-comp\u003e\n\u003cscript type=\"module\"\u003e\n  import moduleA from 'module-a';\n  import moduleB from 'module-a';\n  export default {\n    setup() {\n      console.log(moduleA, moduleB);\n    }\n  }\n\u003c/script\u003e\n```\n\n#### Resolve lazily / on demand\n\nIn case the defined component *imports* something from an external file, like `import module from './js/module.js'` would do, such import would be lazily resolved, together with any other module that is not known yet, meaning that `./js/module.js` file could contain something like this:\n\n```js\n// a file used to bootstrap uce-template component\n// dependencies can always use the uce-template class\nconst {resolve} = customElements.get('uce-template');\n\n// resolve one to many modules\nresolve('quite-big-module', {...});\n```\n\nA component script can then import this file and access its exported modules right after.\n\n[Live demo](https://webreflection.github.io/uce-template/test/resolve.html)\n\n```html\n\u003cscript type=\"module\"\u003e\n  import './js/module.js';\n  import quiteBigModule from 'quite-big-module';\n  export default {\n    setup() {\n      console.log(quiteBigModule);\n    }\n  }\n\u003c/script\u003e\n```\n\nTogether with *lazy loaded component*, this approach makes it possible to ship components that are fully based on an external `vue/comp.uce` file definition, where any of these components can also share one or more `.js` files able to *resolve* any module needed here or there (shared dependencies in one file, as opposite of dependencies per each shipped components).\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n- - -\n\n## F.A.Q.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWhy is the polyfill included?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nAs standalone file, my [Custom Elements](https://github.com/ungap/custom-elements#readme) size is around *2.1K*, but since it's share almost every library *uce* uses too, bundling it together looked like the best way to go, resulting in just *1K* extra for a module that fits in roughly *7K* to *10K* budget.\n\nOn the other hand, because the polyfill is not obtrusive and based on runtime features detections, this means that nobody should care about bringing any other polyfill ever, but also *Chrome*, *Firefox*, and *Edge*, will be untouched, so that every custom element will run natively, either builtin extend or regular.\n\nIn the *Safari* case, or *WebKit* based, only custom elements builtin are provided, while in *IE11* and the old *MS Edge*, both builtin extends and regular elements are patched.\n\nThat's it: don't worry about any polyfill, because everything is already included in here!\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWhat if I target modern browsers only?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nIf you are targeting browsers you know already provide native Custom Elements V1, you can use [this ESM version](https://unpkg.com/uce-template@latest/es.js) that excludes all polyfills and include only the logic.\n\nThe current `es.js` bundle is indeed *~7K* gzipped and *~6.5K* brotli, so that it's possible to save even extra bandwidth in your project.\n\n#### But my browser is Safari/WebKit ...\n\nWell, in such case if that's the only target browser, the [@webreflection/custom-elements-builtin](https://github.com/WebReflection/custom-elements-builtin#readme) module must be included *before* the *uce-template* module lands on the page.\n\n```html\n\u003cscript defer src=\"//unpkg.com/@webreflection/custom-elements-builtin\"\u003e\u003c/script\u003e\n\u003cscript defer src=\"//unpkg.com/uce-template\"\u003e\u003c/script\u003e\n```\n\nThis will ensure both regular and builtin extends will work as expected.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWhy there's no ShadowDOM polyfill?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nUnfortunately *ShadowDOM* is one of those specifications impossible to polyfill, but the good news is that you'll rarely need *ShadowDOM* in *uce-template*, but if your browser is compatible, you can use *ShadowDOM* as much as you like.\n\n*However*, there are at least two possible partial polyfills to consider: [attachshadow](https://github.com/WebReflection/attachshadow#readme), which is minimalistic and lightweight, and [ShadyDOM](https://github.com/webcomponents/polyfills/tree/master/packages/shadydom#readme), which is closer to standards, but definitively heavier, although both polyfills can, and should, be injected *only* if the current browser needs it, so sticking this code on top of your *HTML* page would bring *ShadowDOM* to IE11 too, or others.\n\n```html\n\u003c!-- this must be done before uce-template --\u003e\n\u003cscript\u003e\nif(!document.documentElement.attachShadow)\n  document.write('\u003cscript src=\"//unpkg.com/attachshadow\"\u003e\u003c\\x2fscript\u003e');\n\u003c/script\u003e\n\u003cscript defer src=\"//unpkg.com/uce-template\"\u003e\u003c/script\u003e\n```\n\nAs every modern browser will have `document.documentElement.attachShadow`, the `document.write` will happen *only* in *IE11* without ever compromise, or penalize, Mobile and modern Desktop browsers.\n\n**P.S.** the `\u003c\\x2fscript\u003e` is not a typo, it's needed to not have a broken layout due closing *script* tag\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWhy using \u003ccode\u003e{{...}}\u003c/code\u003e instead of \u003ccode\u003e${...}\u003c/code\u003e?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nAs much as I would've loved to have `${...}` interpolation boundaries, *IE11* would break if an element in the DOM contains `${...}` as attribute.\n\nBecause `{{...}}` is a well established alternative, I've decided to avoid monkey-patching possible *IE11* issues and simply stick with a de-facto standard alternative.\n\nIt is also worth considering that *Vue* uses `{{...}}` too, and so do many other template based engines.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWhy is \u003ccode\u003eFunction\u003c/code\u003e necessary?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nAs explained in the \"*CSP \u0026 integrity/nonce*\" part of the [how to/examples](#how-to--examples), it is necessary to use `Function` for at least two reasons:\n\n  * it's the only way to opt out from `\"use strict\";` directive and pass through a `with(object)` statement, needed to understand interpolations without creating a whole JS engine from the scratch\n  * it's the only way to provide at runtime a CJS like `require` functionality within `\u003cscript type=\"module\"\u003e` content\n\nBut even if there was no `Function` in the equation, parsing and executing a `\u003cscript\u003e` tag to define custom elements would've been the exact same equivalent of using `Function`, because *CSP* would've needed special rules anyway, since the operation is basically an *eval* call in the global context.\n\nAs summary, instead of tricking the browser with practices that are as safe, or as unsafe, as a `Function` call, I've simply used `Function` instead, keeping the code size reasonable.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWhat about performance?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThis project is *as-performant-as* native Custom Elements could be, except for the definition cost, which is a *one-off* operation per each unique custom element *Class*, hence irrelevant in the long run, and there's an insignificant overhead within the initial template parsing logic, but its repeated execution is as fast as *uhtml* can be, and if you [check the latest status](https://rawgit.com/krausest/js-framework-benchmark/master/webdriver-ts-results/table.html) you'll find it's one of the fastest of its kind.\n\nYou can check the classic [DBMonster demo here](https://webreflection.github.io/uce-template/test/dbmonster.html), and see that it performs just well.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eAre there blocking requests with modules?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nNothing in this library is blocking, and modules are resolved *once* only, even relative path imports.\n\nThe logic is pretty simple: if the module name has not been resolved and it's a relative import, an asynchronous request will be made and evaluated later, while if the module is not resolved, and it's a qualified name, it will be resolved only once some code provides it.\n\nAll this, plus the *import* to *require* resolution, is handled by the [uce-require](https://github.com/WebReflection/uce-require) helper, purposely not coupled with this module itself, as it could hopefully inspire, and be used by, other projects too.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n- - -\n\n## Live Demos\n\n  * a [todo app](https://github.com/WebReflection/uce-template-todo-2020) forked from the *Vue* version of [this post](https://medium.com/javascript-in-plain-english/i-created-the-exact-same-app-in-react-and-vue-here-are-the-differences-2019-edition-42ba2cab9e56)\n  * the classic [DB Monster](https://webreflection.github.io/uce-template/test/dbmonster.html) demo\n  * a *Custom Element in uce-template* example, either [via getters](https://codepen.io/WebReflection/pen/MWyRGbd?editors=1000), or via the [component context itself](https://codepen.io/WebReflection/pen/BaKEPJR?editors=1000)\n\n- - -\n\n\n## ... and more!\n\nIf you'd like to understand more about `uce-template` and how does it work, please check [this page](./extra-details.md) out.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebreflection%2Fuce-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwebreflection%2Fuce-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebreflection%2Fuce-template/lists"}