Architecture

This document explains the internal architecture of InAppAI React, helping you understand how the component works and make informed decisions when building with it.

Overview

InAppAI React is built on three core principles:

  1. Controlled Mode - Parent component manages all state
  2. Backend Separation - API keys and AI logic stay on the server
  3. Client-Side Tools - Tool handlers execute in the browser

Component Architecture

┌─────────────────────────────────────────────────┐
           Your React Application                
                                                 
  ┌────────────────────────────────────────┐    
           <InAppAI />                        
                                              
    ┌──────────────┐  ┌──────────────┐       
       UI Layer      State Layer         
                                         
      Button         messages          
      Window         isOpen            
      Messages       isLoading         
      Input          error             
    └──────────────┘  └──────────────┘       
                                              
    ┌──────────────────────────────────┐     
          Tool Registry                    
       Executes handlers locally          
       Returns results to backend         
    └──────────────────────────────────┘     
  └────────────────────────────────────────┘    
                       HTTP                    
└──────────────────────┼─────────────────────────┘
                       
         ┌─────────────────────────┐
            InAppAI Backend       
                                  
            Validates requests   
            Calls AI API         
            Manages context      
            Handles tool calls   
         └─────────────────────────┘
                       
              ┌────────────────┐
                 AI Provider  
                (OpenAI, etc) 
              └────────────────┘

Data Flow

Message Send Flow

1. User types message and presses Enter
   
2. Component creates Message object
   
3. Calls onMessagesChange([...messages, userMessage])
   
4. Parent updates state (re-renders with new message)
   
5. Component sends HTTP POST to backend
   (includes full conversation history + current message)
   
6. Backend forwards conversation history + message to AI API
   
7. AI responds (with optional tool calls)
   
8. If tool calls:
   a. Component executes tool handlers locally
   b. Records each call in toolActions array
   c. Sends results back to backend (with conversation history)
   d. Backend gets natural language response
   
9. Component calls onMessagesChange([...messages, assistantMessage])
   (assistantMessage includes toolActions if tools were used)
   
10. Parent updates state (re-renders with AI response)

Controlled Mode

InAppAI React uses controlled mode exclusively:

// The component NEVER manages messages internally
// Parent must provide both messages and onChange

function App() {
  const [messages, setMessages] = useState<Message[]>([]); // ← Parent owns state

  return (
    <InAppAI
      messages={messages}              // ← Props IN
      onMessagesChange={setMessages}   // ← Callback OUT
    />
  );
}

Why controlled mode?

  • You control persistence (localStorage, backend, etc.)
  • You can transform messages before storing
  • Enables multi-conversation apps
  • Full control over state management
  • Works with any state library (Redux, Zustand, etc.)

Conversation Memory

The component sends the full messages array to the backend with each API request, giving the AI complete conversation history. This means the AI can:

  • Reference earlier messages (“As I mentioned before…”)
  • Remember what it was asked and what it answered
  • Recall tool actions it performed in previous turns

When assistant messages include tool executions, the component appends structured tool action context to the conversation history:

[Tool actions: addTodo({"text":"Buy groceries"}) → {"success":true}, completeTodo({"identifier":"Buy groceries"}) → {"success":true}]

This enables the AI to recall not just what it said, but exactly what actions it took and their outcomes.

What the AI sees per request:

[system] You are a helpful assistant... Context: {todos: [...]}
[user] Add a task called "Buy groceries"
[assistant] I've added "Buy groceries" to your list!
  [Tool actions: addTodo({"text":"Buy groceries"}) → {"success":true}]
[user] What did you just do?   ← Current message

Tool Execution Model

Tools use a local-first, iterative execution model:

1. User: "Add 3 todos: Buy milk, Call dentist, Finish report"
   
2. Backend receives message + tool definitions
   
3. AI decides to call tools (may return multiple tool calls)
   
4. Backend returns: { toolCalls: [...] }
   
5. Component executes ALL tool handlers locally (in parallel)
   
6. Component sends results back to backend WITH tool definitions
   
7. AI checks results:
   a. If more actions needed  returns more tool calls (go to step 5)
   b. If all done  returns natural language response
   
8. Loop continues until AI responds with text only,
   or maxToolRounds is reached
   
9. Component calls onMessagesChange([...messages, assistantMessage])

The maxToolRounds prop (default: 10) limits how many rounds the loop can execute, preventing runaway tool calling.

Why local execution?

  • Direct access to React state
  • No need to expose state mutation APIs
  • Tools can trigger side effects (navigate, open modals, etc.)
  • Type-safe with TypeScript

Context Handling

Context is evaluated at message send time:

// Static context - captured once
<InAppAI
  context={{ userId: '123' }}  // ← Object evaluated once on mount
/>

// Dynamic context - fresh on every message
<InAppAI
  context={() => ({
    scrollPosition: window.scrollY,  // ← Function called each send
    selectedText: window.getSelection()?.toString(),
  })}
/>

State Management

The component manages several internal states:

UI State (Internal)

const [isOpen, setIsOpen] = useState(false);        // Window open/closed
const [isFolded, setIsFolded] = useState(false);    // Sidebar folded
const [inputValue, setInputValue] = useState('');   // Input text
const [isLoading, setIsLoading] = useState(false);  // AI thinking
const [error, setError] = useState<string | null>(null);

Message State (External)

// Provided by parent via props
messages: Message[]
onMessagesChange: (messages: Message[]) => void

Each Message in the array may include:

  • content - The text of the message
  • usage - Token usage statistics (assistant messages)
  • toolActions - Record of tool calls and results (assistant messages that used tools)

This separation ensures:

  • UI state is temporary (doesn’t need persistence)
  • Message state is durable (parent controls storage)
  • Tool execution history is preserved with messages

Rendering Strategy

InAppAI React uses different render strategies per display mode:

return (
  <>
    <button onClick={toggleOpen}>🤖</button>  {/* Fixed position */}
    {isOpen && (
      <div className="window">  {/* Absolute positioned */}
        {/* Chat UI */}
      </div>
    )}
  </>
);
return (
  <div className="sidebar">  {/* Fixed position, full height */}
    {isFolded ? (
      <div className="folded-content">AI</div>
    ) : (
      <div className="expanded-content">{/* Chat UI */}</div>
    )}
  </div>
);

Panel Mode

return (
  <div className="panel" style={{ width: panelWidth }}>
    <div className="resize-handle" onMouseDown={startResize} />
    {/* Chat UI */}
  </div>
);

Embedded Mode

// Returns chat UI directly (no wrapper)
return (
  <div className="chat-window">
    {/* Chat UI */}
  </div>
);

Bundle Size

InAppAI React is optimized for small bundle size:

  • Main package: ~50KB (minified)
  • Dependencies:
    • react-markdown: ~25KB
    • react-syntax-highlighter: ~20KB
  • Total: ~95KB minified (~30KB gzipped)

Tree-shaking eliminates unused code.

Browser Compatibility

Supports all modern browsers:

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+

Uses standard Web APIs:

  • fetch for networking
  • localStorage for persistence (user-implemented)
  • CSS Grid/Flexbox for layout

TypeScript Support

Fully typed with TypeScript:

  • All props have type definitions
  • Message, Tool, CustomStyles interfaces exported
  • Generic types for custom contexts
  • Type inference for tool handlers
import type { InAppAIProps, Message, Tool } from '@inappai/react';

Next Steps