Skip to content

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:

  1. <atheon-input>: Tracks prompt formulation, hesitation, and submission.
  2. <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;
    };
  }
}
* Vue: Add the tags to 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.