Products

Components

Components are reusable building blocks that combine logic and UI, serving as the foundation of LemonadeJS applications. This section explores the core concepts and patterns essential for creating custom components.

Overview

Components in LemonadeJS follow an instance-based architecture, where this serves as the context for accessing component properties and methods. Each component integrates template literals for defining the UI structure with reactive state management, enabling efficient DOM updates without requiring full component re-renders.

Component Declaration

Global Declaration

Components can be registered globally using the setComponents method, making them accessible across all templates in your application. Component names should follow the PascalCase convention to distinguish them from native HTML elements in templates.

function Hello() {
    return render => render`<div>${this.year}</div>`;
}

// Register the custom component to be used on across your application
lemonade.setComponents({ Hello });

// Now any component can utilize the component <Hello/>
function Component() {
    // Year
    this.value = 2025;
    // Year is a prop create on the Hello scope
    return render => render`<div>
        <Hello year="${this.value}" />
    </div>`;
}

Component References

Components can be passed directly as references within templates, offering a flexible alternative to global registration. This method supports localized component usage and encourages more modular and maintainable code organization.

function Hello() {
    return render => render`<div>${this.year}</div>`;
}

function Component() {
    // Year
    this.value = 2025;
    // Update
    const update = () => {
        this.value++;
    }
    // title and year will be available inside Hello through (this)
    return render => render`<div>
        <${Hello} year="${this.value}" />
        <input type="button" value="Next" onclick="${update}" />
    </div>`;
}

Local Declaration

In LemonadeJS, you can declare and use components locally by utilizing lemonade.element. Here's an example:

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
// Component
function Test() {
    return render => render`<p>Hello ${this.type}</p>`;
}

function Form() {
    // Declare the components locally
    let template = `<Test type="World" />`;

    return lemonade.element(template, this, { Test });
}
// Render the component
lemonade.render(Form, document.getElementById('root'));
</script>
</html>
import lemonade from 'lemonadejs';

// Component
function Test() {
    return render => render`<p>Hello ${this.type}</p>`;
}

export default function Form() {
    // Declare the components locally
    let template = `<Test type="World" />`;

    return lemonade.element(template, this, { Test });
}

Using JSX

JSX simplifies the process of defining and utilizing components, making it more transparent and declarative. The example below demonstrates how to pass properties to a child component using JSX:

function Hello() {
    return (<div>{this.year}</div>);
}

function Component() {
    // Year
    this.value = 2025;
    // title and year will be available inside Hello through (this)
    return (
        <div>
            <Hello year={this.value} />
        </div>
    );
}

Props

Props are values passed to components, enabling communication between parent and child components. In LemonadeJS, props are automatically bound to the component instance and are accessible through the this context.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
function Crypto() {
    return render => render `<div>
        When <b>${this.title}</b> reaches <b>${this.value}</b>?
    </div>`;
}

// Register custom tags
lemonade.setComponents({ Crypto });

function Component() {
    // The innerHTML of Crypto is the template for the component
    return render => render`<div>
        <Crypto title="Bitcoin" value="250000" />
    </div>`;
}
// Render the component
lemonade.render(Component, document.getElementById('root'));
</script>
</html>
import lemonade from 'lemonadejs';

function Crypto() {
    return render => render `<div>
        When <b>${this.title}</b> reaches <b>${this.value}</b>?
    </div>`;
}

// Register custom tags
lemonade.setComponents({ Crypto });

export default function Component() {
    // The innerHTML of Crypto is the template for the component
    return render => render`<div>
        <Crypto title="Bitcoin" value="250000" />
    </div>`;
}

More About Props

For more detailed information about props, please visit:

Component Props

Children

Overview

LemonadeJS v5 exposes child component instances to the parent’s lifecycle hooks before DOM mounting. This pre-rendering access allows direct manipulation of child properties, implementation of conditional rendering logic, and custom initialization, all while preserving reactive bindings between parent and child components.

Child Component Rendering

Child components are instantiated and attached to the parent’s root element during rendering, establishing the component hierarchy.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
// Template is based on the caller innerHTML
function Crypto(children) {
    // This array is available before the component is created and its children appended to the root element of the component
    console.log(children);
    // Create the component
    return render => render `<div data-title=${this.title}></div>`;
}

function Component() {
    // The innerHTML of Crypto is the template for the component
    return render => render`<div>
        <Crypto title="Bitcoin">
            <b>Coin: USD 250.000</b>
        </Crypto>
    </div>`;
}
// Register custom tags
lemonade.setComponents({ Crypto });
// Render the component
lemonade.render(Component, document.getElementById('root'));
</script>
</html>
import lemonade from 'lemonadejs';

// Template is based on the caller innerHTML
function Crypto(children) {
    // This array is available before the component is created and its children appended to the root element of the component
    console.log(children);
    // Create the component
    return render => render `<div data-title=${this.title}></div>`;
}

// Register custom tags
lemonade.setComponents({ Crypto });

export default function Component() {
    // The innerHTML of Crypto is the template for the component
    return render => render`<div>
        <Crypto title="Bitcoin">
            <b>Coin: USD 250.000</b>
        </Crypto>
    </div>`;
}

Component Reactivity

Reactive Properties on this

LemonadeJS enables reactivity for properties defined on the component’s this context when they are bound to the template literal. Changes to these properties are automatically detected and reflected in the DOM, provided they are used in dynamic expressions or direct references within the template, eliminating the need for manual re-rendering.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
// Component
function Test() {
    this.status = false;

    const update = () => {
        this.status = ! this.status;
    }
    return render => render`<div>
        <p>${this.status && `<b>test</b>`||''}</p>
        <input type="button" value="Toggle" onclick="${update}" />
    </div>`;
}
// Render the component
lemonade.render(Test, document.getElementById('root'));
</script>
</html>
export default function Test() {
    this.status = false;

    const update = () => {
        this.status = ! this.status;
    }
    return render => render`<div>
        <p>${this.status && `<b>test</b>`||''}</p>
        <input type="button" value="Toggle" onclick="${update}" />
    </div>`;
}

Reactive State Objects

States are also reactive objects but operate independently of the this context. In a similar way, when bound to the template literal, changes to their value property are automatically tracked and seamlessly updated in the DOM, also without additional re-rendering calls.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
let { state } = lemonade;

function Test() {
    let status = state(false);

    const update = () => {
        status.value = ! status.value;
    }
    return render => render`<div>
        <p>${(status.value && `<b>test</b>`)||''}</p>
        <input type="button" value="Toggle" onclick="${update}" />
    </div>`;
}
// Render the component
lemonade.render(Test, document.getElementById('root'));
</script>
</html>
import { state } from 'lemonadejs';

export default function Test() {
    let status = state(false);

    const update = () => {
        status.value = ! status.value;
    }
    return render => render`<div>
        <p>${(status.value && `<b>test</b>`)||''}</p>
        <input type="button" value="Toggle" onclick="${update}" />
    </div>`;
}

Reserved Context Properties

When a custom component is instantiated in LemonadeJS, certain properties within the this context are reserved for core functionalities:

Property Description
el Represents the root HTML element of the component.
refresh A method to trigger a re-render of the component.

Component Refresh

The refresh method in a component is designed to force updates to the DOM. It can be used to refresh specific properties, the entire view, or the entire component.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
// Component
function Test() {
    this.test = [1,2,3];

    const update = () => {
        this.test[2] = 30;
        // Will re-render the elements in the template that uses the property test. 
        // Can be useful when change array or objects that does not have an internal tracking
        this.refresh('test');
    }
    
    return render => render`<div>
        <p>${this.test}</p>
        <input type="button" value="Toggle" onclick="${update}" />
    </div>`;
}
// Render the component
lemonade.render(Test, document.getElementById('root'));
</script>
</html>
export default function Test() {
    this.test = [1,2,3];

    const update = () => {
        this.test[2] = 30;
        // Will re-render the elements in the template that uses the property test. 
        // Can be useful when change array or objects that does not have an internal tracking
        this.refresh('test');
    }

    return render => render`<div>
        <p>${this.test}</p>
        <input type="button" value="Toggle" onclick="${update}" />
    </div>`;
}

Summary of this Chapter

Key points to remember:

  • Attributes Access: Component attributes are accessible via this.
  • Reserved Properties: parent, el, and refresh are reserved for core functionality.
  • Component Refresh: This enables updating the component while preserving its state.
  • Declaration: Components can be declared globally using setComponents.

What's Next?

Learn more about props and how to pass arguments to a component.

Learn more about props