Alpine.js

repository·main·Indexed 12 days ago

https://github.com/alpinejs/alpine

A rugged, minimal JavaScript framework for composing behavior directly in markup. It provides a reactive data model and a set of directives to manage DOM state, with version 3.16.1 featuring a core engine and plugins for collapse, CSP, focus, history, intersect, mask, morph, and persist.

Tokens
54.8K
Snippets
251
Records
268
Agent score
98%

What's inside Alpine.js

  1. Overview of Alpine.js packages and plugins

    main

    Alpine.js is managed as a monorepo using npm workspaces. The core functionality resides in alpinejs, while additional features are provided via plugins.

    Key packages include:

    • alpinejs: The core Alpine engine.
    • collapse: Smooth animations for expanding/collapsing elements.
    • csp: A build designed to be Content Security Policy (CSP) safe.
    • focus: Focus management within elements.
    • history: Binds data to query string parameters via the History API.
    • intersect: Triggers expressions when elements intersect the viewport.
    • mask: Automatic text input formatting.
    • morph: Intelligent HTML morphing.
    • persist: Persists Alpine state across page loads.
  2. Recap of core Alpine.js directives

    main

    The following core directives form the foundation of Alpine.js development:

    • x-data: Defines a new scope of data for a component.
    • x-on: Listens for browser events (e.g., x-on:click="...").
    • x-text: Sets the element's textContent to the given expression.
    • x-show: Toggles the visibility of an element using display: none.
    • x-model: Creates two-way data binding on input, select, and textarea elements.
    • x-for: Iterates over an array to render a list of elements.
  3. Use init() and destroy() lifecycle methods

    main

    Alpine components can implement lifecycle methods to manage side effects:

    • init(): Automatically executed by Alpine before the component renders. Use this for setup or registering event listeners.
    • destroy(): Automatically executed before Alpine cleans up the component. Use this to prevent memory leaks by detaching event handlers or clearing timers (e.g., clearInterval or removeEventListener).
    Alpine.data('timer', () => ({
        timer: null,
        counter: 0,
        init() {
          // Register an event handler
          this.timer = setInterval(() => {
            console.log('Increased counter to', ++this.counter);
          }, 1000);
        },
        destroy() {
            // Detach the handler to avoid memory leaks
            clearInterval(this.timer);
        },
    }))
  4. Correct <template> structure for x-for

    main

    Because x-for requires the <template> to have exactly one root element, you cannot place multiple sibling elements directly inside the template. You must wrap them in a single container element (like a <div>, <p>, or <span>).

    Incorrect:

    <template x-for="color in colors">
        <span>Text</span><span>Value</span>
    </template>

    Correct:

    <template x-for="color in colors">
        <p>
            <span>Text</span><span>Value</span>
        </p>
    </template>
  5. Automatically evaluate init() functions in V3

    main

    In Alpine V3, if your data object contains a method named init(), Alpine will automatically call it during initialization. You no longer need to manually call init() via x-init.

    <!-- 🚫 Before -->
    <div x-data="foo()" x-init="init()"></div>
    
    <!-- ✅ After -->
    <div x-data="foo()"></div>
    
    <script>
        function foo() {
            return {
                init() {
                    // Automatically called
                }
            }
        }
    </script>
  6. Deep watching with $watch

    main

    When you watch an object (e.g., $watch('foo', ...)), Alpine.js automatically detects changes to any nested property within that object.

    Important behavior: When a change is detected in a sub-property, the watcher returns the value of the observed property (the whole object), not just the specific sub-property that changed.

    <div x-data="{ foo: { bar: 'baz' } }" x-init="$watch('foo', (value, oldValue) => console.log(value, oldValue))">
        <button @click="foo.bar = 'bob'">Update</button>
    </div>
  7. Communicate between components using .window

    main

    Because events bubble up the DOM tree, components that are not in a direct parent-child relationship (such as siblings) cannot capture each other's dispatched events through standard bubbling.

    To allow components to communicate across different parts of the DOM, use the .window modifier on the listener. This instructs the listener to catch the event at the window level, where all bubbling events eventually arrive.

    <!-- Component A: Listening at the window level -->
    <div x-data @notify.window="console.log('Caught!')"></div>
    
    <!-- Component B: Dispatching the event -->
    <button @click="$dispatch('notify')">Notify</button>
  8. Understand component scope and nesting

    main

    Properties defined in an x-data directive are available to all child elements of that component. When nesting components using x-data, child components can access properties from parent components, but if a child defines a property with the same name as a parent, the child's property will shadow the parent's within that child's scope.

    <div x-data="{ foo: 'bar' }">
        <span x-text="foo"></span> <!-- Outputs: "bar" -->
    
        <div x-data="{ bar: 'baz' }">
            <span x-text="foo"></span> <!-- Outputs: "bar" (from parent) -->
    
            <div x-data="{ foo: 'bob' }">
                <span x-text="foo"></span> <!-- Outputs: "bob" (shadowed) -->
            </div>
        </div>
    </div>
  9. Declare reactive data with x-data

    main

    The x-data directive is the starting point for almost all Alpine components. It is used to declare a scope of data by passing a plain JavaScript object. Alpine tracks the properties within this object, and any changes to them will trigger updates in the DOM where those properties are used.

    x-data must be placed on a parent element for other Alpine directives within that element to function.

    <div x-data="{ count: 0 }">
        <!-- Data properties are available to children -->
    </div>
  10. Nesting and scoping data in Alpine

    main

    Alpine data is nestable. When you place an element with x-data inside another element that also has x-data, the child element can access the parent's data. This follows standard scoping rules:

    1. Accessing Parents: A child element can reference properties defined in any ancestor's x-data.
    2. Shadowing: If a child element defines a property with the same name as a parent's property, the child's property takes precedence within that child's scope.
    <div x-data="{ open: false }">
        <div x-data="{ label: 'Content:' }">
            <span x-text="label"></span>
            <span x-show="open"></span>
        </div>
    </div>