Skip to content

Events and state

Loquix components communicate with standard attributes, DOM properties, methods, and custom events. Your application remains responsible for conversations, requests, persistence, and provider credentials.

This page covers that DOM contract — the attributes, properties, and events every component exposes — independent of how you drive them. The Integration section covers AgentController, the piece that now exists to drive the chat seam of that contract for you, so you do not have to wire loquix-submit to a raw fetch call by hand the way the example below still does.

Inputs

Pass primitive configuration through HTML attributes and complex data through DOM properties.

Intent

Listen for loquix-* events such as submit, stop, selection, feedback, and attachment removal.

State

Update your application state, then reflect the result back into component properties and attributes.

Strings, numbers, and true boolean flags work naturally in markup:

<loquix-chat-composer
placeholder="Ask anything…"
max-length="4000"
streaming
></loquix-chat-composer>

Boolean attributes are true when present, regardless of their text value. Do not write streaming="false"; remove the attribute or assign the property:

composer.streaming = false;

Assign structured data after selecting the element:

const selector = document.querySelector('loquix-model-selector');
selector.models = [
{
value: 'atlas-pro',
label: 'Atlas Pro',
description: 'Complex reasoning',
capabilities: ['reasoning', 'vision'],
},
];

HTML attributes serialize values to strings, so they are not suitable for models, modes, suggestions, attachments, or other object arrays.

Loquix events use the loquix- prefix and are dispatched with bubbles: true and composed: true. Listen on the component, chat container, application shell, or document:

const chat = document.querySelector('loquix-chat-container');
chat.addEventListener('loquix-submit', event => {
console.log(event.detail.content);
});
chat.addEventListener('loquix-model-change', event => {
console.log('Changed from', event.detail.from, 'to', event.detail.to);
});

Event payloads live in event.detail. Component reference pages document each payload shape.

Use events as intent, not as persistence:

attachments.addEventListener('loquix-attachment-remove', event => {
const removedId = event.detail.attachment.id;
state.attachments = state.attachments.filter(
attachment => attachment.id !== removedId,
);
attachments.attachments = state.attachments;
});

This pattern keeps components compatible with any state manager and makes server synchronization explicit.

const controller = new AbortController();
chat.addEventListener('loquix-submit', async event => {
composer.streaming = true;
try {
const response = await fetch('/api/chat', {
method: 'POST',
signal: controller.signal,
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ content: event.detail.content }),
});
if (!response.ok) throw new Error('Request failed');
// Append or update the assistant message from the response.
} finally {
composer.streaming = false;
}
});
chat.addEventListener('loquix-stop', () => controller.abort());

Importing @loquix/core types augments HTMLElementEventMap with typed Loquix events:

import type {
LoquixSubmitDetail,
LoquixModelChangeDetail,
} from '@loquix/core';
document.addEventListener('loquix-submit', event => {
const content: string = event.detail.content;
});

For element-specific properties, import the class type:

import type { LoquixModelSelector } from
'@loquix/core/classes/loquix-model-selector';
const selector = document.querySelector(
'loquix-model-selector',
) as LoquixModelSelector;
  • Render user and model strings with textContent unless they have been sanitized.
  • Keep AI provider keys and privileged requests on your server.
  • Validate dropped files again on the server; the browser accept filter is a usability feature.
  • Treat URLs and metadata from model output as untrusted input.
  • Use stable message and attachment IDs instead of DOM position for updates.