Quick Start

Three examples, each building on the last. All runnable in the browser - no build step needed.

1. A Counter

Define a component, render it, update state with this.$():

define('my-counter', {
    $({ count = 0 }) {
        return [
            mx.button({ onclick: _ => this.$({ count: count - 1 }) }, '−'),
            mx.span({ class: 'count' }, count),
            mx.button({ onclick: _ => this.$({ count: count + 1 }) }, '+')
        ];
    }
});

document.getElementById('demo').render(mx('my-counter'));

define() registers a custom element. $() receives props (merged with state). this.$({ count: count + 1 }) updates state and re-renders.

Learn more about components →

2. User Cards

A component that takes props, computes derived values, and renders conditionally:

define('user-card', {
    $({ name, role = 'Member', avatar }) {
        let initials = name.split(' ').map(w => w[0]).join('');
        return mx.div({ class: 'card', style: 'display:flex;gap:12px;align-items:center;padding:16px;border:1px solid #333;margin:4px 0' },
            avatar
                ? mx.img({ src: avatar, style: 'width:40px;height:40px;border-radius:50%' })
                : mx.div({ style: 'width:40px;height:40px;border-radius:50%;background:#f0a828;display:flex;align-items:center;justify-content:center;font-weight:600;color:#111' }, initials),
            mx.div(
                mx.div({ style: 'font-weight:500' }, name),
                mx.div({ style: 'font-size:13px;color:#888' }, role)
            )
        );
    }
});

let container = document.getElementById('demo');
container.render(
    mx('user-card', { name: 'Alice Chen', role: 'Engineer' }),
    mx('user-card', { name: 'Bob Smith', role: 'Designer' }),
    mx('user-card', { name: 'Carol Wu', role: 'PM' })
);

Props with defaults (role = 'Member'), conditional rendering (avatar ? ... : ...), computed values (initials).

Learn more about elements & rendering →

3. A Dynamic List

No component needed - just a function that renders into a container:

let items = ['Learn mx.js', 'Build something', 'Ship it'];

function renderList(target) {
    target.render(
        mx.h3('Tasks (' + items.length + ')'),
        mx.div({ style: 'display:flex;gap:8px;margin:8px 0' },
            mx.input({ id: 'new-item', placeholder: 'New task...', style: 'flex:1;padding:6px' }),
            mx.button({ onclick: _ => {
                let input = target.querySelector('#new-item');
                if (!input.value.trim()) return;
                items.push(input.value);
                input.value = '';
                renderList(target);
            }}, 'Add')
        ),
        mx.ul(
            ...items.map((item, i) =>
                mx.li({ style: 'display:flex;justify-content:space-between;padding:4px 0' },
                    mx.span(item),
                    mx.button({ onclick: _ => {
                        items.splice(i, 1);
                        renderList(target);
                    }, style: 'border:none;background:none;color:#ef4444;cursor:pointer' }, '\u00d7')
                )
            )
        )
    );
}

renderList(document.getElementById('demo'));

Plain functions work great for simple UI. render() reconciles the list efficiently - only changed items update.

Learn more about lists, tables & patterns →

Next Steps