
Security News
Re-Enabled GitHub Actions Expose Thousands of Repositories to Mini Shai-Hulud
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.
@matterchat/vue
Advanced tools
The Matter Chat support widget as a Vue component. One prop, no snippet.
The Matter Chat support widget as a Vue component.
One prop, no snippet, no useHead script entry to write by hand.
Also available: @matterchat/react, @matterchat/svelte, @matterchat/angular.
pnpm add @matterchat/vue
<script setup lang="ts">
import { MatterChat } from "@matterchat/vue";
</script>
<template>
<RouterView />
<MatterChat public-key="pk_live_…" />
</template>
Your public key is in Setup in the dashboard. It is safe in client code: it identifies the bot, it does not authorise anything.
The component renders nothing — its render function returns null, which Vue
places in the DOM as an empty comment node. It is a side effect with a
component's shape, so it drops into App.vue without disturbing the layout.
Wrap it in <ClientOnly>:
<ClientOnly>
<MatterChat :public-key="config.public.matterchatKey" />
</ClientOnly>
All of the work happens in onMounted and a post-flush watcher, neither of
which runs during a server render, so the component is already safe to import
on the server. <ClientOnly> is belt and braces: it keeps the component out of
the server render entirely, so a future change on either side cannot make it
touch window or the document during SSR.
If you know who the visitor is, you can tell the widget — but only your server
can make that claim believable. id on its own is a label the visitor could
have typed; hash is your backend vouching for it.
<MatterChat
:public-key="key"
:user="{ id: user.id, hash: session.chatHash, expiresAt: session.chatHashExpiresAt }"
/>
hash is an HMAC over <id>:<expiresAt> keyed on the bot's identity secret.
That secret must never reach the browser — compute the hash on your server
and pass the result down. Without a hash that verifies, the visitor is treated
as anonymous.
Requires the Verified visitor identity feature on your plan.
The same package also exports useMatterChat, for a help page or docs site
that wants an ask box instead of a launcher. Same public key, same knowledge,
same inbox.
<script setup lang="ts">
import { ref } from "vue";
import { useMatterChat } from "@matterchat/vue";
const { messages, ask, streaming, error, status, warm } = useMatterChat({ publicKey: "pk_live_…" });
const q = ref("");
function submit() {
ask(q.value);
q.value = "";
}
</script>
<template>
<form @submit.prevent="submit">
<input v-model="q" placeholder="Ask anything" :disabled="streaming" @focus="warm" />
<p v-if="status === 'connecting'">Connecting…</p>
<p v-for="(m, i) in messages" :key="i">{{ m.content }}</p>
<p v-if="error" role="alert">{{ error.message }}</p>
</form>
</template>
Every field is a read-only ref, so it reads the same in a template as
anything else you own.
The question shows at once and the answer grows token by token. Each assistant
message carries sources (the pages it cited, once each), retrieved (what
its [n] markers point at), refusal, lowConfidence and handoff.
rate(1 | -1) rates the latest answer and reset() starts over, stopping an answer
still streaming. Leaving the page does the same, so an abandoned answer does
not spend a reply. A question that fails stays on screen with failed: true,
and asking again replaces it.
Nothing connects until warm runs or the first question is asked; status
says connecting meanwhile. parseBlocks is re-exported for rendering an
answer's Markdown the way the widget does. Full reference in @matterchat/core's README
and at https://matterchat.co/docs/headless.
| Prop | Type | |
|---|---|---|
publicKey | string | Required. The bot's public key. |
user | { id, hash, expiresAt } | Verified visitor identity. Omit for anonymous. |
appUrl | string | Defaults to https://app.matterchat.co. For a preview or self-hosted install. |
In a template these are public-key, user and app-url. Write public-key
as a plain attribute for a literal and bind it (:public-key="key") only when
the value is a variable.
There are deliberately no appearance props — no title, greeting, accent, position or suggestions. All of it is configured in the dashboard and delivered with the bot's config. A prop that overrode the dashboard would be a second source of truth, and the first support ticket would begin "I changed the colour and nothing happened".
Injects one <script> pointing at a content-hashed loader, with a matching
integrity hash and crossorigin="anonymous", then queues init so ordering
never matters. The loader asks the app which widget bundle is current and adds
it with the hash the server supplies for it, so your browser still refuses to
run a bundle whose bytes do not match a hash — the hash simply arrives per page
load rather than being frozen into this package.
It is safe to render in more than one place — the script is added once, and the
widget ignores a second init. It does not remove the widget on unmount:
tearing it out mid-conversation, and re-downloading the bundle on every
client-side navigation, are both worse than leaving it.
The props are watched. A public-key that is empty at mount time — a runtime
config or store that resolves after the first render — does nothing until it
fills in, and then loads the widget. Changing props on an instance that has
already loaded the widget queues another init, which the widget ignores.
The work happens in onMounted and a post-flush watcher, neither of which runs
during a server render, so a server render emits only an empty comment node and
touches neither window nor the document.
Each release pins one loader, not one widget build. Widget fixes and features reach you without a release of this package, because the loader resolves the current bundle at run time. What you give up is that we can change the widget's code underneath you between releases. What you keep is the part that stops a tampered CDN: nothing executes unless its bytes match a hash.
WIDGET_LOADER is exported for inspection only — the component reads it for
you.
Vue 3. No dependencies.
FAQs
The Matter Chat support widget as a Vue component. One prop, no snippet.
The npm package @matterchat/vue receives a total of 1 weekly downloads. As such, @matterchat/vue popularity was classified as not popular.
We found that @matterchat/vue demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.

Research
/Security News
The compromise affects MemTensor's MemOS, an open source memory framework for large language models (LLMs) and AI agents. Both npm package @memtensor/memos-cloud-openclaw-plugin and the PyPI package MemoryOS are compromised. They drop cross-platform Go binaries that exfiltrate developer secrets.