slim.js Documentation

repository·master·Indexed 21 days ago

https://github.com/slimjs/slim.js

An ultra-fast, lightweight (under 3KB gzipped), and standards-compliant library for developing Web Components. slim.js provides a declarative, reactive template system without a virtual DOM or heavy compiler, featuring a reactive template system using double curly braces, a plugin system for lifecycle hooks, and support for custom directives via DirectiveRegistry.

Tokens
12.2K
Snippets
50
Records
57
Agent score
77%

What's inside slim.js

  1. Core concepts of slim.js

    master

    slim.js is a declarative web components library built on modern web standards. Key characteristics include:

    • Reactivity: It uses Handlebars-style syntax to create bindings. It efficiently updates the DOM only when relevant properties change.
    • Lightweight Core: The core is less than 3kB gzipped. It scans HTML for Handlebars syntax and executes it within the component's scope.
    • Extensibility: You can add custom directives to the registry or create global plugins that hook into the component lifecycle.
    • Standard-Compliant: Built on custom-elements technology, making components reusable across different environments without interference from other frameworks.
  2. How Slim templates and data binding work

    master

    Slim templates use Handlebars-style syntax ({{...}}) to bind data to the DOM.

    When you use {{this.propertyName}} within a template, slim.js automatically wraps that property with a getter and setter. When the property value changes on the component instance, slim.js detects the change and automatically updates the corresponding text node or attribute in the DOM.

    Note that some directives can execute code without requiring Handlebars syntax and do not require property change detection.

  3. How reactiveness works in slim.js

    master

    slim.js components use a template static property containing HTML with handlebars-like syntax (e.g., {{this.propertyName}}) to declare reactive data bindings.

    Key Reactivity Rules:

    • Prefixing: All component properties must be accessed using the this.* prefix inside templates.
    • Immutability: slim.js assumes data structures are immutable to avoid expensive dirty-checking. If you mutate an object internally, the DOM will not update. To trigger an update after mutation, you must re-set the property to itself (e.g., this.userInfo = this.userInfo) or use Utils.forceUpdate.
    • Style Binding: You can bind data directly into <style> nodes to reactively update CSS values.
    <header>
      <span class="user-info">
        <img class="avatar" src="{{this.userInfo.profileImage}}" />
        {{this.userInfo.username}}
      </span>
    </header>
  4. Understand the Slim class lifecycle callbacks

    master

    The Slim class extends HTMLElement and provides both native custom-element callbacks and additional artificial lifecycle hooks. These artificial hooks are useful because template directives (like *if or *foreach) might postpone rendering or creation phases; using the Slim hooks ensures your code runs at the correct time relative to the component's internal state.

    Creation and Rendering Phase

    • onBeforeCreated(): Executes before the shadowDOM and bindings are created. Use this to initialize data (e.g., ensuring properties are not undefined) before bindings are executed.
    • onCreated(): The template is ready and all directives have been executed, but the component's HTML content is still empty. Use this for asynchronous initialization like loading data.
    • onRender(): The component has rendered for the first time. Use this when you need to access child elements or DOM nodes that were created during the render phase.

    DOM Attachment Phase

    • onAdded(): Invoked when the component is added to the DOM (wraps Slim.connectedCallback).
    • onRemoved(): Invoked when the component is removed from the document (wraps Slim.disconnectedCallback).
    @tag('my-element')
    @template(
      `<span>{{this.myValue}}</span><input #ref="myButton" disabled type="button" click="addOne" />`
    )
    class MyElement extends Slim {
      onBeforeCreated() {
        // ensures that myValue is not undefined, just before the bindings are executed
        this.myValue = 1;
      }
    
      onCreated() {
        // element created and bound to parent custom elements, the content is still not attached
        this.loadSomeData().then((data) => doSomethingUseful(data));
      }
    
      onRender() {
        // access to children is available
        this.myButton.disabled = false;
      }
    
      onAdded() {
        console.log('Added');
      }
    
      onRemoved() {
        console.log('Removed');
      }
    }
  5. How directives work in slim.js

    master
    Directives are optional, standalone middleware modules that execute code on your template based on specific attribute prefixes. They allow you to extend the capabilities of your HTML markup. All directives (except the default custom-code directive) are optional modules that you can opt-in to use.
  6. Use plugins to hook into component lifecycles

    master
    You can extend the functionality of any slim.js component by using plugins. Plugins allow you to hook into any step of a component's lifecycle, enabling you to execute custom code or modify component behavior dynamically.
  7. Quickstart: Create a Web Component with slim.js

    master

    To create a reactive Web Component, extend the Slim class and use decorators for defining the custom element tag and its HTML template. Slim uses a declarative syntax where code wrapped in double curly braces {{ ... }} is executed reactively. When properties used in the template change, only the specific bound DOM nodes are updated.

    import { Slim } from 'slim-js';
    import { tag, template } from 'slim-js/decorators';
    
    @tag('my-awesome-element')
    @template(`
    <button @click="this.inc()"> + </button>
    <span>{{this.count}}</span>
    <button @click="this.dec()"> - </button>
    `)
    class extends Slim {
      count = 0;
      inc() { this.count++ }
      dec() { this.count-- }
    }
  8. Opt-out of Shadow DOM in slim components

    master

    By default, slim.js components use Shadow DOM to encapsulate styles and render content. If you need to render content in the Light DOM (for example, to allow global CSS to affect component internals or for easier integration with certain legacy tools), you can opt-out of Shadow DOM using one of three methods: a static getter, a static property, or the @useShadow decorator.

    // Method 1: Static getter
    class MyTag extends Slim {
      static get useShadow() {
        return false
      }
    }
    
    // Method 2: Static property
    class MyOtherTag extends Slim {}
    MyOtherTag.useShadow = false;
    
    // Method 3: Decorator
    import {useShadow} from 'slim-js/decorators';
    
    @useShadow(false)
    class MyDecoratedTag extends Slim {}