Framework-agnostic AI chat, as composable Web Components. Drop an
<aparte-chat> onto any page — React, Vue, Svelte, Angular, or none at all —
wire it to any LLM through a pluggable transport, and restyle everything with CSS
variables. Zero dependencies at the core.
🚧 Pre-alpha. The API is stabilising and the first
0.0.xalpha is not on npm yet — thenpm installlines below are how it will work at release. Watch the repo to follow along.
pronounced ah-par-té — French aparté: a line spoken aside, a private word taken "in aparté". Also reads as a part: one composable piece.
- Framework-agnostic core. One engine — vanilla Web Components — renders the
same chat everywhere. Thin wrappers give React/Vue/Svelte/Angular an
ergonomic component; with no framework, use the
<aparte-*>custom elements directly. - Zero dependencies in
@aparte/core. Markdown, syntax highlighting, model pickers — all opt-inprovider-*/plugin-*packages. The core stays tiny. - Bring your own model, your way. A transport seam decides where the
request goes:
DirectTransport(browser-direct — BYOK or a local model) orBackendTransport(your/api/chat, key stays server-side). Providers cover the OpenAI-compatible family, the Vercel AI SDK (25+ vendors), and in-browser Transformers.js. - Streaming, typed segments, tools. Replies stream as typed segments — text,
markdown, code, chain-of-thought — including a
tool_callsegment with a built-in human-in-the-loop approve/reject gate. - Yours to restyle. Theme everything through CSS variables (no forking), swap icons and render hooks, and localise the UI strings (English built-in, French shipped).
- A library, not an app. No routing, settings, or persistence baked in, and backend-agnostic — it's a chat surface you compose, not a product you inherit.
npm install @aparte/core @aparte/provider-openai-compat<aparte-chat center-empty placeholder="Ask anything…" style="height: 600px"></aparte-chat>import '@aparte/core'; // registers the <aparte-*> custom elements
import '@aparte/core/styles.css'; // theme variables + component styles
import { registerDefaultRenderers, AparteConfig, AparteClient, DirectTransport } from '@aparte/core';
import { createOpenAICompatProvider, presets } from '@aparte/provider-openai-compat';
registerDefaultRenderers();
// A local model (LM Studio / Ollama) needs no key — just enable CORS in the app.
// Swap in presets.OPENAI / .MISTRAL / .OPENROUTER (+ a keyResolver) for a cloud vendor.
AparteConfig.registerAIProvider(createOpenAICompatProvider(presets.LMSTUDIO));
AparteConfig.setTransport(new DirectTransport({ byok: true }));
new AparteClient().start(); // listens for sends, streams the reply into the chat
// The bare shell streams the assistant reply; echo the user's own message in:
const chat = document.querySelector('aparte-chat');
chat.addEventListener('aparte-send', (e) =>
chat.viewport.appendMessage({ id: crypto.randomUUID(), role: 'user', content: e.detail.content, timestamp: Date.now() }),
);A real streaming, bring-your-own-key chat — no backend, no build magic. → Getting started
Same core, an ergonomic component per framework — and the wrapper owns the state and the user bubble, so there's even less to wire:
npm install @aparte/react @aparte/core react react-domimport { AparteChat, useAparteChat, useAparteClient } from '@aparte/react';
import { AparteConfig, DirectTransport } from '@aparte/core';
import { createOpenAICompatProvider, presets } from '@aparte/provider-openai-compat';
import '@aparte/core/styles.css';
AparteConfig.registerAIProvider(createOpenAICompatProvider(presets.OPENROUTER));
AparteConfig.setTransport(new DirectTransport({ byok: true }));
export function Chat() {
const chat = useAparteChat();
useAparteClient(); // bridges composer sends to the model
return <AparteChat ref={chat.ref} messages={chat.messages} onMessagesChange={chat.setMessages} centerWhenEmpty />;
}| Framework | Package | Guide |
|---|---|---|
| React 18 / 19 | @aparte/react |
React |
| Vue 3 | @aparte/vue |
Vue |
| Svelte | @aparte/svelte |
Svelte |
| Angular 19 | @aparte/angular |
Angular |
Runnable examples for every framework (plus vanilla) live in
apps/playgrounds.
| Package | What |
|---|---|
@aparte/core |
Vanilla web components — the chat engine, zero dependencies |
@aparte/engine |
Framework-agnostic agent loop (runStreamAgent) |
@aparte/react · /vue · /svelte · /angular |
Thin, ergonomic framework wrappers (peer deps) |
@aparte/provider-openai-compat |
One adapter for every OpenAI-compatible endpoint (OpenAI, Mistral, OpenRouter, Groq, LM Studio, Ollama…) |
@aparte/provider-ai-sdk |
Vercel AI SDK bridge (Anthropic, Google, 25+ vendors) |
@aparte/provider-transformers |
In-browser inference via Transformers.js |
@aparte/plugin-marked · -streaming-markdown · -shiki |
Markdown rendering + syntax highlighting |
@aparte/plugin-model-selector · -ask-question |
A provider/model picker; a question-elicitation UI |
@aparte/locale-fr |
French UI strings (English is core's built-in default) |
The docs are a Starlight site in
apps/docs (run pnpm docs locally):
- Getting started — your first streaming chat, no framework
- Providers — connect a real model (OpenAI-compatible, AI SDK, Transformers.js)
- Theming — restyle everything through CSS variables
- Customization — icons, render hooks, action registries
- Conversations & branching — retry, edit, branches, persistence
- The agent engine — the headless
runStreamAgentloop
Built in the open. The core, engine, four wrappers, providers, plugins and six
runnable playgrounds are in place and green (unit tests + a browser E2E suite
across every framework). Next up: the first coordinated 0.0.x alpha on npm.
Issues and PRs welcome — see CONTRIBUTING.md for conventions and the gate each change lands behind. It's a pnpm + NX monorepo:
pnpm install
pnpm build # all packages
pnpm test # unit suite (Vitest)
pnpm e2e # browser smoke E2E (Playwright; run pnpm e2e:install once)
pnpm docs # the docs siteMIT © Paul Richez