Wrapper surface
Every slot and every callback the React, Vue, Svelte and Angular wrappers expose, generated from the wrapper source.
The default shell of <AparteChat> leaves you these places to put your own markup. The
same slots exist on all four wrappers — that is checked mechanically on every pnpm gate,
not promised in prose — and each framework declares them in its own idiom.
| Slot | React | Vue | Svelte | Angular |
|---|---|---|---|---|
composer | composer={…} | <template #composer> | <svelte:fragment slot="composer"> | slot="composer" |
empty-state | emptyState={…} | <template #empty-state> | <svelte:fragment slot="empty-state"> | slot="empty-state" |
above-composer | aboveComposer={…} | <template #above-composer> | <svelte:fragment slot="above-composer"> | slot="above-composer" |
toolbar | toolbar={…} | <template #toolbar> | <svelte:fragment slot="toolbar"> | slot="toolbar" |
What each one is for
Section titled “What each one is for”composer
Section titled “composer”Custom composer content, rendered inside <aparte-composer> in place of the default shell (input · send, plus the attachment primitives when attachments is set).
React:
<AparteChat composer={<MyThing />} />Vue:
<AparteChat>
<template #composer><MyThing /></template>
</AparteChat>Svelte:
<AparteChat>
<svelte:fragment slot="composer"><MyThing /></svelte:fragment>
</AparteChat>Angular:
<aparte-chat>
<div slot="composer"><app-my-thing /></div>
</aparte-chat>empty-state
Section titled “empty-state”Welcome / placeholder content shown INSIDE the viewport while there are no messages (a real “empty state” region, not a workaround via aboveComposer).
React:
<AparteChat emptyState={<MyThing />} />Vue:
<AparteChat>
<template #empty-state><MyThing /></template>
</AparteChat>Svelte:
<AparteChat>
<svelte:fragment slot="empty-state"><MyThing /></svelte:fragment>
</AparteChat>Angular:
<aparte-chat>
<div slot="empty-state"><app-my-thing /></div>
</aparte-chat>above-composer
Section titled “above-composer”Content rendered ABOVE the composer (e.g. a disclaimer banner, a “scroll to bottom” affordance, a context chip).
React:
<AparteChat aboveComposer={<MyThing />} />Vue:
<AparteChat>
<template #above-composer><MyThing /></template>
</AparteChat>Svelte:
<AparteChat>
<svelte:fragment slot="above-composer"><MyThing /></svelte:fragment>
</AparteChat>Angular:
<aparte-chat>
<div slot="above-composer"><app-my-thing /></div>
</aparte-chat>toolbar
Section titled “toolbar”The composer’s bottom row — a mode picker, a model selector, a token counter.
React:
<AparteChat toolbar={<MyThing />} />Vue:
<AparteChat>
<template #toolbar><MyThing /></template>
</AparteChat>Svelte:
<AparteChat>
<svelte:fragment slot="toolbar"><MyThing /></svelte:fragment>
</AparteChat>Angular:
<aparte-chat>
<div slot="toolbar"><app-my-thing /></div>
</aparte-chat>| Prop | React | Vue | Svelte | Angular |
|---|---|---|---|---|
loading | loading={…} | :loading | loading | [loading] |
Draw the wait yourself while your store answers; the controller sets it for its own fetches.
Callbacks
Section titled “Callbacks”The same events on all four wrappers, each in its own idiom. React, Vue and Angular hand you
the payload directly. Svelte has a callback prop, with the payload, and a component event as well —
on:messageSent and the rest — with it under event.detail.
| Payload | React | Vue | Svelte | Angular |
|---|---|---|---|---|
AparteSendEventDetail | onMessageSent | @message-sent | onmessageSent | (messageSent) |
AparteActionEventDetail | onAction | @action | onaction | (action) |
AparteMessage[] | onMessagesChange | @messages-change | onmessagesChange | (messagesChange) |
AparteMessage | onMessageAppended | @message-appended | onmessageAppended | (messageAppended) |
boolean | onTypingChange | @typing-change | ontypingChange | (typingChange) |
string | onConversationCreated | @conversation-created | onconversationCreated | (conversationCreated) |
What each callback is for
Section titled “What each callback is for”messageSent
Section titled “messageSent”Notification that the user submitted a message from the composer.
action
Section titled “action”Fired when a custom bubble action (registered via aparteGlobalConfig.registerAction with zones: ['bubble']) is clicked — a typed wrapper over the bubbling aparte-action DOM event.
messagesChange
Section titled “messagesChange”Fired when the active message path changes (branch navigation / edit / retry / streaming).
messageAppended
Section titled “messageAppended”Fired when a message is appended internally (e.g. by AparteClient).
typingChange
Section titled “typingChange”Fired when the typing/“thinking” indicator should toggle.
conversationCreated
Section titled “conversationCreated”Fired when the controller lazily creates a conversation on first send.
Two things this table does not cover
Section titled “Two things this table does not cover”A custom bubble is a render hook, not a slot: React takes a renderBubble function,
Vue and Svelte a bubble slot that receives the message, and Angular a
[bubbleTemplate] with the message as $implicit. It is per-message rather
than per-region, which is why it is shaped differently — see
Custom bubbles.
Placement inside a slot is yours. Nothing here is positional: put your controls in the
order you want them and push one to the end with margin-inline-start: auto — a logical
property, so it follows the reading direction. The worked example is in
The composer toolbar.
provideAparte is Angular’s, and only Angular’s
Section titled “provideAparte is Angular’s, and only Angular’s”Angular alone needs an app initializer that runs before the first component, and a
DestroyRef to release the theme listener it registers. That is what
provideAparte() is. There is no provideAparte in the other
three, and that is not a gap: everything it configures is a plain function call — bar
theme: 'auto', which is a matchMedia listener you own — so React, Vue and Svelte make
the same calls at module scope, before the app mounts.
import { aparteGlobalConfig } from '@aparte/core';
import { createOpenAICompatProvider, presets } from '@aparte/provider-openai-compat';
import { fr } from '@aparte/locale-fr';
aparteGlobalConfig.registerAIProvider(createOpenAICompatProvider(presets.OPENROUTER));
aparteGlobalConfig.setModelConfig({ defaultModel: 'openai/gpt-4o-mini' });
aparteGlobalConfig.setLocale(fr);
document.documentElement.setAttribute('data-aparte-theme', 'dark');
// 'auto' is the one option with no one-liner: match prefers-color-scheme, rewrite the
// attribute on change, and drop the listener when your app tears down.
// The client stays the wrapper's own: useAparteClient() in React and Vue,
// createAparteClient() in Svelte.The day a second wrapper needs that theme listener with a disposer of its own,
applyThemeMode moves into @aparte/core and all four call it — one implementation, four
idioms. Until then it lives where its one caller is.