Client-Side Integration
The Presentation Layer is responsible for rendering the content and powering your user interaction metrics.
Atheon provides two framework-agnostic Web Components that seamlessly handle the entire generation lifecycle:
<atheon-input>: Tracks prompt formulation, hesitation, and submission.<atheon-output>: Tracks read dwell time, text copying, streaming interruptions, link clicks, and agent outcomes.
1. Load the Script
Add the Atheon loader to the <head> of your application (or your root layout file). Copy your publisher key directly from the Atheon Gateway Dashboard.
<script
data-atheon-publisher-key="YOUR_PUBLISHER_KEY"
src="https://js.atheon.ad/atheon.js"
defer
></script>
2. Track the Input
Wrap your prompt input area (<textarea> or <input>) and your send button with <atheon-input>. Add the data-atheon-submit attribute to your send button so Atheon knows when the user commits the prompt.
(Note: Atheon automatically detects standard Enter key presses inside text areas.)
<atheon-input>
<textarea placeholder="Ask the AI..."></textarea>
<button data-atheon-submit>Send</button>
</atheon-input>
3. Track the Output
Wrap your text renderer (Markdown, HTML, or plain text) with the <atheon-output> tag.
You must pass the interaction-id, prompt-hash, and fingerprint returned by your backend Atheon SDK. This securely ties the frontend engagement data to the backend LLM event.
import React from 'react';
import Markdown from 'react-markdown';
interface Props {
llmResponse: string;
interactionId: string;
promptHash: string;
fingerprint: string;
}
export const ChatMessage = ({ llmResponse, interactionId, promptHash, fingerprint }: Props) => {
return (
<atheon-output
interaction-id={interactionId}
prompt-hash={promptHash}
fingerprint={fingerprint}
>
<Markdown>{llmResponse}</Markdown>
</atheon-output>
);
};
<template>
<atheon-output
:interaction-id="interactionId"
:prompt-hash="promptHash"
:fingerprint="fingerprint"
>
<div v-html="llmResponse"></div>
</atheon-output>
</template>
<script setup>
defineProps(['llmResponse', 'interactionId', 'promptHash', 'fingerprint']);
</script>
<script>
export let llmResponse;
export let interactionId;
export let promptHash;
export let fingerprint;
</script>
<atheon-output
interaction-id={interactionId}
prompt-hash={promptHash}
fingerprint={fingerprint}
>
{@html llmResponse}
</atheon-output>
<atheon-output id="chat-bubble">
<div id="text-content"></div>
</atheon-output>
<script>
// Assume these came from your backend API response
const { reply, interaction_id, prompt_hash, fingerprint } = backendResponse;
document.getElementById('text-content').innerText = reply;
const bubble = document.getElementById('chat-bubble');
bubble.setAttribute('interaction-id', interaction_id);
bubble.setAttribute('prompt-hash', prompt_hash);
bubble.setAttribute('fingerprint', fingerprint);
</script>
Advanced Output Features
You can add specific attributes to <atheon-output> or its descendants to capture granular user behaviors.
Streaming
If your response is streaming in real-time, add the streaming attribute to the container. Remove the attribute when the stream finishes.
<atheon-output interaction-id="..." prompt-hash="..." fingerprint="..." streaming>
Action Buttons
Add these data attributes to buttons inside the output to track user lifecycle actions:
data-atheon-stop: Tracks when a user interrupts a stream.data-atheon-regenerate: Tracks when a user requests a new response.
Agent Outcomes
If you are evaluating Agent performance, add the agent-output attribute to the main container, and use data-atheon-outcome on your feedback buttons:
<atheon-output interaction-id="..." prompt-hash="..." fingerprint="..." agent-output>
<p>Response text...</p>
<button data-atheon-outcome="approved">👍</button>
<button data-atheon-outcome="rejected">👎</button>
</atheon-output>
Troubleshooting
The tags are not recognized
If you are using React or Vue, you may see a console warning about an unknown element. This is harmless, but you can suppress it:
- React: No action needed (React 19+). For older versions or TypeScript, add a declaration:
declare namespace JSX {
interface IntrinsicElements {
'atheon-input': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
'atheon-output': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
'interaction-id'?: string;
'prompt-hash'?: string;
'fingerprint'?: string;
'streaming'?: boolean;
'agent-output'?: boolean;
};
}
}
compilerOptions.isCustomElement in your vite.config.js:
vue({ template: { compilerOptions: { isCustomElement: tag => tag.startsWith('atheon-') } } })
Styles not applying?
Both <atheon-input> and <atheon-output> uses Shadow DOM to prevent style leakage. If you want your global CSS to affect the content inside, use CSS custom properties (variables) or inject a <style> tag into the shadow root.