The Jean Memory React SDK provides two powerful ways to integrate: a simple, out-of-the-box UI component for rapid development, and a flexible hook for building completely custom experiences.

Getting Context: The getContext Function

The primary way to retrieve context from Jean Memory is with the getContext function. It provides four distinct speed modes, allowing you to choose the right balance of speed and quality for your application.
const { getContext } = useJean();

// Fast: Direct memory search (0.5-1s)
const fastContext = await getContext("What was my recent idea about AI?", { mode: 'fast' });

// Balanced: AI synthesis with Gemini 2.5 Flash (3-5s) - Recommended
const balancedContext = await getContext("How should I approach this project?", { mode: 'balanced' });

// Autonomous: Intelligent orchestration with adaptive decision-making (variable latency)
const smartContext = await getContext("Help me plan my week based on my goals", { mode: 'autonomous' });

// Comprehensive: Deep document analysis (20-30s)
const deepContext = await getContext("Analyze my writing style across my recent essays.", { mode: 'comprehensive' });

Speed Modes

  • fast: Direct memory search returning raw memories. Ideal for real-time applications where speed is critical.
  • balanced: AI synthesis using Gemini 2.5 Flash for conversational responses. Recommended for most use cases.
  • autonomous: Intelligent orchestration that adaptively determines context analysis depth. May take longer for complex reasoning.
  • comprehensive: Deep document analysis with extensive memory search. Best for research and detailed analysis.
Learn more about speed modes →

Installation

npm install @jeanmemory/react

Three Integration Approaches

The Jean Memory React SDK provides three levels of integration complexity, allowing you to choose the right approach for your use case.

Level 1: Complete Drop-in Solution (2 lines)

The fastest way to get a full-featured chatbot with authentication running in your app.
import { JeanProvider, JeanChatComplete } from '@jeanmemory/react';

export default function Home() {
  return (
    <JeanProvider apiKey={process.env.NEXT_PUBLIC_JEAN_API_KEY}>
      <JeanChatComplete />
    </JeanProvider>
  );
}
The JeanChatComplete component includes everything you need out of the box:
  • Automatic OAuth 2.1 PKCE authentication flow
  • Complete chat interface with professional styling
  • Sign-in and sign-out buttons
  • Loading states and error handling
  • Welcome message and example prompts
Styling Options:
<JeanChatComplete 
  placeholder="Ask me anything..."
  welcomeMessage="Welcome to your AI assistant"
  examplePrompts={["What's my schedule?", "Remember this meeting"]}
  showSignOut={true}
  showExamples={true}
  chatHeight={500}
  style={{ maxWidth: '600px', margin: '0 auto' }}
  className="my-custom-chat"
  header={<CustomHeader />}
/>
Current Limitations: JeanChatComplete uses inline styles for rapid prototyping. For production applications requiring extensive brand customization, consider Level 2 or Level 3 approaches.

Level 2: Authentication Guard with Custom UI (5 lines)

When you want your own UI but simplified authentication management.
import { JeanProvider, JeanAuthGuard, useJean } from '@jeanmemory/react';

export default function Home() {
  return (
    <JeanProvider apiKey={process.env.NEXT_PUBLIC_JEAN_API_KEY}>
      <JeanAuthGuard>
        <MyCustomChatApp />
      </JeanAuthGuard>
    </JeanProvider>
  );
}

function MyCustomChatApp() {
  const { sendMessage, messages, user, signOut } = useJean();
  // Your custom UI implementation
  return <div>Your custom chat interface</div>;
}
JeanAuthGuard automatically handles:
  • Loading states while checking authentication
  • Sign-in UI when user is not authenticated
  • Displaying your custom app when authenticated
  • Customizable loading and fallback components
Customization Options:
<JeanAuthGuard
  loadingComponent={<div>Checking authentication...</div>}
  fallback={<CustomSignInPage />}
  showDefaultSignIn={false}
>
  <MyApp />
</JeanAuthGuard>

Level 3: Full Control (Advanced)

Complete control over authentication flow and UI implementation.

Authentication: Completely Automated

The React SDK uses a robust Universal OAuth 2.1 PKCE system that handles all authentication complexity for you. Users get secure, persistent sessions with zero configuration required from developers.

What You Get Automatically:

  • OAuth 2.1 PKCE Flow - Industry-standard security with Google authentication
  • Universal Identity - Same user account across all Jean Memory applications
  • Session Persistence - Users stay logged in across browser refreshes and tabs
  • Automatic Token Management - JWT tokens handled invisibly
  • Error Recovery - Graceful handling of network issues and token expiration

Simple Setup for Any API Key:

<JeanProvider apiKey="jean_sk_your_api_key">
  <JeanChat /> {/* Automatically handles authentication */}
</JeanProvider>
For Production Apps: Users will see a “Sign In with Jean” button that triggers secure OAuth authentication. For Development/Testing: Test API keys (containing test) automatically create isolated test users, so you can start coding immediately without any authentication setup.
import { JeanProvider, SignInWithJean, useJean } from '@jeanmemory/react';

function App() {
  return (
    <JeanProvider apiKey="jean_sk_your_api_key">
      <AuthenticatedApp />
    </JeanProvider>
  );
}

function AuthenticatedApp() {
  const { sendMessage, isAuthenticated, user, signOut } = useJean();
  
  if (!isAuthenticated) {
    return (
      <SignInWithJean 
        onSuccess={(user) => console.log('Signed in:', user)}
      >
        Custom Sign In Button
      </SignInWithJean>
    );
  }

  // Your completely custom app implementation
  return (
    <div>
      <h1>Welcome, {user?.name}</h1>
      <button onClick={signOut}>Sign Out</button>
      {/* Your custom chat interface */}
    </div>
  );
}

The useJean Hook

The useJean hook provides access to all core functionality for building custom interfaces.
const {
  // Authentication State
  isAuthenticated: boolean,
  isLoading: boolean,
  user: JeanUser | null,
  
  // Conversation State
  messages: JeanMessage[],
  error: string | null,
  
  // Core Methods
  sendMessage: (message: string, options?: MessageOptions) => Promise<void>,
  signOut: () => void,
  clearConversation: () => void,
  
  // Document Management
  storeDocument: (title: string, content: string) => Promise<void>,
  connect: (service: 'notion' | 'slack' | 'gdrive') => void,
  
  // Direct Memory Tools
  tools: {
    add_memory: (content: string) => Promise<any>;
    search_memory: (query: string) => Promise<any>;
    deep_memory_query: (query: string) => Promise<any>;
    store_document: (title: string, content: string, document_type?: string) => Promise<any>;
  };
} = useJean();

Building a Custom Chat Interface

Example implementation showing authentication handling and message management:
import { useJean, SignInWithJean } from '@jeanmemory/react';
import { useState } from 'react';

function CustomChatInterface() {
  const { isAuthenticated, messages, sendMessage, signOut, user } = useJean();
  const [input, setInput] = useState('');

  if (!isAuthenticated) {
    return (
      <div className="auth-container">
        <h2>Sign in to continue</h2>
        <SignInWithJean onSuccess={(user) => console.log('Welcome:', user.email)}>
          Sign In with Jean
        </SignInWithJean>
      </div>
    );
  }

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!input.trim()) return;
    
    try {
      await sendMessage(input);
      setInput('');
    } catch (error) {
      console.error('Failed to send message:', error);
    }
  };

  return (
    <div className="chat-container">
      <header>
        <span>Welcome, {user?.name}</span>
        <button onClick={signOut}>Sign Out</button>
      </header>
      
      <div className="messages">
        {messages.map((message) => (
          <div key={message.id} className={`message ${message.role}`}>
            <strong>{message.role === 'user' ? 'You' : 'Jean'}:</strong>
            {message.content}
          </div>
        ))}
      </div>
      
      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask Jean anything..."
        />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}

Configuration Options (Optional)

For 99% of use cases, the defaults work perfectly. But when you need control:
const { sendMessage } = useJean();

// Speed-optimized (faster, less comprehensive)
await sendMessage("What's my schedule?", { speed: "fast" });

// Different tools for specific needs  
await sendMessage("What's my schedule?", { tool: "search_memory" });

// Simple text response instead of full metadata
await sendMessage("What's my schedule?", { format: "simple" });

Advanced Use or Legacy Implementations: Direct Tool Access

Primary Method: For most use cases, we strongly recommend using the new getContext function to retrieve context. It provides a simpler interface with predictable performance.
For advanced use cases where you need fine-grained control over Jean Memory, you can use the tools namespace from the useJean hook.
import { useJean } from '@jeanmemory/react';

function AdvancedComponent() {
  const { tools, isAuthenticated } = useJean();
  
  const handleDirectMemoryAdd = async () => {
    if (!isAuthenticated) return;
    
    // Direct tool call
    const result = await tools.add_memory("My dog's name is Max.");
    
    console.log('Memory added:', result);
  };
  
  const handleSearch = async () => {
    if (!isAuthenticated) return;
    
    const results = await tools.search_memory("information about my pets");
    
    console.log('Search results:', results);
  };
  
  // Store documents directly
  const handleStoreDocument = async () => {
    if (!isAuthenticated) return;
    
    await tools.store_document(
      "Meeting Notes",
      "# Team Meeting\n\n- Discussed project timeline\n- Next steps defined",
      "markdown"
    );
  };
  
  // Deep memory queries for complex relationship discovery
  const handleDeepQuery = async () => {
    if (!isAuthenticated) return;
    
    const insights = await tools.deep_memory_query(
      "connections between my preferences and goals"
    );
    
    console.log('Deep insights:', insights);
  };
}

Available Tools

  • tools.add_memory(content) - Add a specific memory
  • tools.search_memory(query) - Search existing memories
  • tools.deep_memory_query(query) - Complex relationship queries
  • tools.store_document(title, content, type?) - Store structured documents
Note: These tools automatically handle authentication and use your user context. No manual token management required.

Session Management

Sign Out

User sign-out is handled automatically through the useJean hook:
function MyComponent() {
  const { signOut, user } = useJean();
  
  const handleSignOut = async () => {
    await signOut();
    // User is automatically signed out and state is cleared
  };

  return (
    <div>
      <span>Welcome, {user?.name}</span>
      <button onClick={handleSignOut}>Sign Out</button>
    </div>
  );
}
The signOut() method:
  • Clears all authentication tokens and session data
  • Resets the authentication state
  • Removes locally stored user information
  • Prepares the application for a fresh sign-in
Alternative Manual Approach:
import { signOutFromJean } from '@jeanmemory/react';

// Direct function call (not recommended for most use cases)
const handleManualSignOut = () => {
  signOutFromJean();
  window.location.reload();
};