Lila JS

The Most Efficient Reactive JavaScript Micro-Framework

Build modern web applications with a minimalist approach: full framework power in just 9kb

Key Advantages of Lila JS as a Micro-Framework

🚀 Ultra-Compact Core

Complete reactive system, routing and components in under 9kb. No bloatware or hidden dependencies.

💡 Precise Reactivity

Granular update system that only modifies what's needed in the DOM using native Proxy.

🧩 Self-Contained Components

Each component manages its own reactive state, lifecycle and scoped template.

🔄 Declarative Binding

Bind data with simple HTML attributes (data-bind, data-model) without JSX.

🧭 Micro-Code Router

SPA navigation system with under 200 bytes of core code. Simple yet powerful.

Instant Boot

Runs immediately in browser with no build steps. Perfect for rapid prototyping.

🔌 UNIX Philosophy API

"Do one thing well" principle. Concise and predictable methods.

📱 Progressive Evolution

Start with a traditional script and scale to full PWA as needed.

🛠️ Full Interoperability

Use only what you need and integrate with any existing stack.

Micro-Framework vs Library vs Full Framework?

Pure Libraries

  • ✔️ Solve specific problems only
  • ❌ Don't provide architecture
  • Examples: Lodash, Axios

Lila JS (Micro-Framework)

  • ✔️ Defined yet flexible architecture
  • ✔️ Core features without over-engineering
  • Advantage: Perfect balance

Full Frameworks

  • ✔️ All-in-one solutions
  • ❌ Complexity and overhead
  • Examples: Angular, Ember

Getting Started

1. Installation

Just include the script in your HTML file:

index.html

<script src="lila.js"></script>
 
<script src="https://seip25.github.io/Lila_js/lila.js"></script>

                        

2. Basic Structure

Here's a minimal setup for a Lila JS application:

index.html

<body>
  <nav>
    <a href="/" data-link>Home</a>
    <a href="/about" data-link>About</a>
  </nav>
  
  <main id="app-lila"></main>
  
  <template data-template="home-template">
    <h1>Welcome</h1>
    <p data-bind="message"></p>
  </template>
  
    <template data-template="about-template">
    <h1>About</h1>
    <p>This is the about us page.</p>
    </template>

  <script>
    App.createComponent('Home', {
      template: 'home-template',
      state: () => ({ message: 'Hello World!' })
    });
    App.addRoute('/', 'Home');

    App.createComponent('About', {
        template: 'about-template'
    });
    
    App.addRoute('/about', 'About');


                            
    handleRouting();
  </script>
</body>
                        

Practical Examples

Counter Component

A simple counter with increment, decrement and reset functionality, plus auto-increment every second.

Template

counter-template.html

    <template data-template="counter-template">
        <h1>Counter</h1>
        <p>Current count: <span data-bind="count"></span></p>
        <button data-action="increment">Increment</button>
        <button data-action="decrement">Decrement</button>
        <button data-action="reset">Reset</button>
    </template>
                        

Component Definition

counter-component.js

    const incrementCounter = ({ state }) => { state.count++ };
        
    App.createComponent('Counter', {
        template: 'counter-template',
        state: () => ({
            count: 0,
            interval: null
        }),
        actions: {
            increment: incrementCounter,
            decrement: ({ state }) => { state.count-- },
            reset: ({ state }) => { state.count = 0 }
        },
        onMount: (state) => {
            console.log('Counter mounted');
            state.interval = setInterval(() => {
                console.log('Interval tick', state.count);
                state.count++;
            }, 1000);
        },
        onDestroy: (state) => {
            console.log('Counter destroyed');
            if (state.interval) {
                clearInterval(state.interval);
                state.interval = null;
            }
        }
    });
    
    App.addRoute('/counter', 'Counter');
                        

Features

  • Reactive State: The count updates automatically in the UI when changed
  • Lifecycle Methods: onMount starts auto-incrementing, onDestroy cleans up
  • Actions: Three user-triggerable actions (increment, decrement, reset)
  • Routing: Accessible at /counter route

About Component

A simple static page component.

Template

about-template.html

    <template data-template="about-template">
        <h1>About</h1>
        <p>This is the about us page.</p>
    </template>
                        

Component Definition

about-component.js

    App.createComponent('About', {
        template: 'about-template'
    });
    
    App.addRoute('/about', 'About');
                        

Not Found (404) Component

A fallback component for unmatched routes with a button to return home.

Template

not-found-template.html

    <template data-template="not-found-template">
        <h1>404 Not Found</h1>
        <p>The page you're looking for doesn't exist.</p>
        <button data-action="goHome">Go Home</button>
    </template>
                        

Component Definition

not-found-component.js

    App.createComponent('NotFound', {
        template: 'not-found-template',
        actions: {
            goHome: () => {
                App.navigateTo('#/');
            }
        }
    });
    
    App.addRoute('*', 'NotFound');
                        

Features

  • Catch-all Route: Matches any unmatched path (*)
  • Navigation Action: goHome action redirects to root
  • Simple UI: Clear 404 message with call to action

Advanced Examples

Home Component with Form and Nested Components

A complete home page with form handling, data binding, and nested components.

Template

home-template.html

<template data-template="home-template">
    <h1>¡Bienvenido a la página de inicio!</h1>

    <form data-action="submit">
        <input type="text" name="greet" data-bind="greet" placeholder="Ingresa tu saludo" class="w-full" />
        <button type="submit" class="contrast">
            <i class="icon-send"></i>Enviar
        </button>
    </form>
    <br />
    <div>
        <p>Tu saludo es: <span data-bind="greet"></span></p>
    </div>
    <div class="input-icon">
        <i class="icon-edit"></i>
        <input type="text" data-model="greet" placeholder="Cambiar saludo" value="">
    </div>
    <button data-action="changeGreeting" class="w-full">
        <i class="icon-check-circle"></i>Cambiar Saludo
    </button>
    <br />
    <div data-component="Counter"></div>
</template>
                        

Component Definition

home-component.js

App.createComponent('Home', {
    template: 'home-template',
    state: () => ({
        greet: null
    }),
    actions: {
        changeGreeting: ({ state }) => {
            state.greet = state.greet === "Hello World!"
                ? "Hola Mundo!"
                : "Hello World!";
            console.log(state.greet);
        },
        submit: ({ state, event }) => {
            event.preventDefault();
            const formData = Object.fromEntries(new FormData(event.target));
            console.log('Form submitted:', formData);
            alert(JSON.stringify(formData));
        }
    }
});

App.addRoute('/', 'Home');
                        

Key Features Demonstrated

Form Handling

The data-action="submit" attribute binds the form submission to the component's submit action.

Two-Way Data Binding

data-model="greet" creates two-way binding between the input and state property.

One-Way Data Binding

data-bind="greet" displays the state value and updates when it changes.

Nested Components

data-component="Counter" embeds the Counter component within the Home component.

Action Handling

data-action="changeGreeting" binds the button click to the component action.

Form Data Processing

The submit action uses FormData to easily process form inputs.

Complete Application Structure

This example shows how all components work together in a complete application.

Main Application Structure

index.html

<header class="container">
    <nav>
        <a href="/" data-link>Inicio</a>
        <a href="/about" data-link>Acerca de</a>
        <a href="/counter" data-link>Contador</a>
    </nav>
</header>

<main id="app-lila" class="container"></main>

<footer></footer>

<!-- All templates would be included here -->
                        

Complete Routing Setup

app.js

// Define all components first
App.createComponent('Home', { /* ... */ });
App.createComponent('About', { /* ... */ });
App.createComponent('Counter', { /* ... */ });
App.createComponent('NotFound', { /* ... */ });

// Set up routes
App.addRoute('/', 'Home');
App.addRoute('/about', 'About');
App.addRoute('/counter', 'Counter');
App.addRoute('*', 'NotFound'); // Catch-all route

// Initialize routing
handleRouting();
                        

Key Patterns

  • Modular Components: Each feature is encapsulated in its own component
  • Centralized Routing: All routes are defined in one place for easy maintenance
  • Error Handling: The NotFound component catches all unmatched routes
  • Consistent Structure: All components follow the same pattern for predictability

Conditional Rendering with data-if

Show or hide elements based on conditions in your component state.

Template Example

about-template.html

            <template data-template="about-template">
                <h1>Acerca de</h1>
                <div data-if="items.length === 0">
                    <p>No hay items.</p>
                </div>
                <div data-if="items.length > 3">
                    <p>Hay más de 3 items.</p>
                </div>
                <div data-if="items.length > 0">
                    <span data-bind="items"></span>
                </div>
            </template>
                        

Component Definition

about-component.js

            App.createComponent('About', {
                template: 'about-template',
                state: () => ({
                    items: []
                }),
                actions: {
                    addItem: ({ state }) => {
                        state.items = [...state.items, `Item ${state.items.length + 1}`];
                    },
                    removeItem: ({ state }) => {
                        state.items.pop();
                    }
                },
                onMount: (state) => {
                    state.items = ['Item 1', 'Item 2', 'Item 3'];
                }
            });
                        

Features

  • Conditional Display: Elements are shown/hidden based on JavaScript expressions
  • Reactive: Automatically updates when state changes
  • Flexible Conditions: Supports any valid JavaScript expression
  • CSS-Based: Uses display: none to hide elements (preserves DOM position)

Directive Reference

Directive Description Example
data-if Conditionally shows/hides element based on expression <div data-if="items.length > 0">...</div>
data-bind Binds element content to state property <span data-bind="count"></span>
data-action Binds DOM events to component actions <button data-action="increment"></button>
data-link Router link navigation <a href="/about" data-link></a>

Best Practices

Keep Conditions Simple

Use simple boolean expressions in data-if for better performance.

Recommended: data-if="isActive"

Avoid: data-if="user.roles.includes('admin') && items.length > 5"

Combine with data-bind

Use data-bind inside conditional blocks to display dynamic content.


            <div data-if="hasItems">
                <span data-bind="itemCount"></span> items
            </div>
                        

Mutually Exclusive Sections

For toggle between views, use complementary conditions.


            <div data-if="isEditing">Edit Mode</div>
            <div data-if="!isEditing">View Mode</div>
                        

List Rendering with data-repeat

Render dynamic lists from your component state with automatic updates.

Template Example

about-template.html

<template data-template="about-template">
    <h1>Acerca de</h1>
    <div class="mt-4" data-if="items.length === 0">
        <p>No hay items.</p>
    </div>
    
    <div data-repeat="items">
        <article class="mt-4 flex between gap-2" data-repeated-item="" secondary>
            <span class="flex between gap-1">
                <span>Index:</span>
                <span data-repeat-bind="index"></span>
            </span>
            <span data-repeat-bind="item.id"></span>
            <span data-repeat-bind="item.name"></span>
            <button data-repeat-action="removeItemId" class="btn-error">
                <i class="icon-delete"></i>
                <span data-repeat-bind="'Remove ' + item.name"></span>
            </button>
        </article>
    </div>
</template>
                        

Component Definition

about-component.js

App.createComponent('About', {
    template: 'about-template',
    state: () => ({
        items: []
    }),
    actions: {
        addItem: ({ state }) => {
            let item = {
                name: 'Item ' + (state.items.length + 1),
                id: state.items.length + 1
            };
            state.items = [...state.items, item];
        },
        removeItem: ({ state }, index) => {
            let items = state.items;
            items.splice(index, 1);
            state.items = [...items];
        },
        removeItemId: ({ state, event, item, index }) => {
            event.preventDefault();
            state.items = state.items.filter((i) => i.id !== item.id);
        }
    },
    onMount: (state) => {
        state.items = [
            { name: 'Item 1', id: 1 },
            { name: 'Item 2', id: 2 },
            { name: 'Item 3', id: 3 }
        ];
    }
});
                        

Features

  • Automatic List Rendering: Creates DOM elements for each item in your array
  • Reactive Updates: Automatically updates when the array changes
  • Item Binding: Access current item with item and index with index
  • Scoped Actions: Bind actions to each item with item context
  • Flexible Content: Supports any HTML structure inside the repeated element

Directive Reference

Directive Description Example
data-repeat Repeats element for each item in array <div data-repeat="items">...</div>
data-repeat-bind Binds content to current item properties <span data-repeat-bind="item.name"></span>
data-repeat-action Binds action with item context <button data-repeat-action="removeItem"></button>
data-repeated-item Marker attribute for repeated items (auto-added) <div data-repeated-item></div>

Best Practices

Use Keys for Dynamic Lists

Include unique identifiers in your items for better performance with dynamic lists.


state.items = [
    { id: 1, name: 'Item 1' },  // Good - has unique id
    { id: 2, name: 'Item 2' }
];
                        

Combine with Conditional Rendering

Use data-if to show empty state messages.


<div data-if="items.length === 0">
    <p>No items found</p>
</div>
<div data-repeat="items">...</div>
                        

Access Item Properties Safely

Check for property existence when binding to avoid errors.


            <span data-repeat-bind="item.name || 'Unnamed'"></span>
                        

Special Variables

Variable Available In Description
item data-repeat-bind, data-repeat-action Current item in the iteration
index data-repeat-bind, data-repeat-action Current index (0-based) in the iteration

data-repeat-action Context

When using data-repeat-action in repeated items, your action handler receives additional context properties:

repeat-action-context.js

// Action signature for repeated items
actions: {
    handleItem: ({ 
        state,     // Component state
        event,     // Original DOM event
        item,      // Current item object from the array
        index      // Current index in the array
    }) => {
        // Action logic here
    }
}
                        

Context Properties Reference

Property Type Description Example Usage
state Object Component state object (read/write) state.items.push(newItem)
event DOM Event Original event that triggered the action event.preventDefault()
item Object Current item from the repeated array console.log(item.id)
index Number Current index in the array (0-based) state.items.splice(index, 1)

Common Patterns

Remove Item by ID

removeItem: ({ state, item }) => {
    state.items = state.items.filter(i => i.id !== item.id);
}
                        
Update Specific Item

updateItem: ({ state, item, index }) => {
    state.items[index] = { ...item, updated: true };
    state.items = [...state.items]; // Trigger reactivity
}
                        
Event Handling with Item Context

handleClick: ({ event, item }) => {
    event.stopPropagation();
    console.log('Clicked item:', item.name);
}
                        

Important Notes

  • Always use event.preventDefault() for form submissions or anchor clicks
  • For array mutations, always create new references to trigger reactivity:
    
    // Good - triggers update
    state.items = state.items.filter(...);
    
    // Bad - won't trigger reactive update
    state.items.splice(index, 1);
                                
  • The item reference is the same object as in the array (not a deep clone)

API Reference

Core Methods

API Reference

// Create a reactive state object
const state = App.reactive({ 
  count: 0 
});

// Create a component
App.createComponent('ComponentName', {
  template: 'template-id',  // ID of the template element
  state: () => ({ ... }),  // Initial state function
  actions: {               // Component methods
    actionName: ({ state, event }, ...args) => { ... }
  },
  onMount: (state) => {    // Called when component mounts
    console.log('Mounted');
  },
  onDestroy: (state) => {  // Called when component unmounts
    console.log('Destroyed');
  }
});

// Routing
App.addRoute('/path', 'ComponentName');
App.navigateTo('/path');   // Programmatic navigation


        handleRouting();//Execute routes
                        

Template Directives

Directive Description Example
data-bind One-way data binding <span data-bind="message"></span>
data-model Two-way data binding for form inputs <input data-model="username">
data-action Bind component method to DOM events <button data-action="submit"></button>
data-component Nested components <div data-component="Child"></div>
data-link Router link <a href="/about" data-link></a>
data-if Conditional <div data-if="items.length >0"></div>