Skip to content
bodhi-sdk-react-integration logo

Bodhi JS SDK React Integration

bodhi-sdk-react-integration

Integrate React+Vite web apps with bodhi-js-sdk for local LLM integration. Use when user asks to: "integrate bodhi", "add bodhi sdk", "connect to bodhi", "setup bodhi provider", "bodhi react integration", "deploy bodhi to github pages", or troubleshoot bodhi-js-sdk connection/auth issues.

SKILL.md

Full skill instructions

Bodhi JS SDK React Integration

Guide for integrating React+Vite applications with bodhi-js-sdk to enable local LLM chat capabilities through the Bodhi Browser ecosystem.

When to Use This Skill

  • User wants to integrate a React app with bodhi-js-sdk
  • User needs to add chat/​LLM capabilities to their React+Vite app
  • User is deploying a bodhi-integrated app to GitHub Pages
  • User is troubleshooting SDK connection, authentication, or streaming issues

Quick Integration Checklist

  1. Install: npm install @bodhiapp/​bodhi-js-react
  2. Register: Create OAuth client at https://developer.getbodhi.app
  3. Wrap App: Add <BodhiProvider authClientId={...}> around your app
  4. Use Hook: Access useBodhi() for client, auth state, and actions
  5. Build UI: Create chat interface with streaming support

Core Concepts

Package Architecture

  • @bodhiapp/​bodhi-js-react - Preset package for web apps (auto-creates WebUIClient)
  • @bodhiapp/​bodhi-js-react-ext - Preset package for Chrome extensions (auto-creates ExtUIClient)
  • Both include React bindings + OpenAI-compatible API

Connection Modes

  • Extension mode: Via Bodhi Browser extension (preferred)
  • Direct mode: Direct HTTP to local server (fallback)
  • SDK auto-detects and switches modes automatically

Authentication

  • OAuth 2.0 + PKCE flow
  • Two auth servers:
    • Dev: https://main-id.getbodhi.app/​realms/​bodhi (allows localhost)
    • Prod: https://id.getbodhi.app/​realms/​bodhi (requires real domain)

Basic Integration Steps

Step 1: Install Package

npm install @bodhiapp/​bodhi-js-react

Step 2: Wrap App with BodhiProvider

// App.tsx
import { BodhiProvider } from '@bodhiapp/​bodhi-js-react';
import Chat from './​Chat';

const CLIENT_ID = 'your-client-id-from-developer.getbodhi.app';

function App() {
  return (
    <BodhiProvider authClientId={CLIENT_ID}>
      <div className="app">
        <h1>My Bodhi Chat App</​h1>
        <Chat />
      </​div>
    </​BodhiProvider>
  );
}

export default App;

Step 3: Create Chat Component

// Chat.tsx
import { useState, useEffect } from 'react';
import { useBodhi } from '@bodhiapp/​bodhi-js-react';

function Chat() {
  const { client, isOverallReady, isAuthenticated, login, showSetup } = useBodhi();
  const [prompt, setPrompt] = useState('');
  const [response, setResponse] = useState('');
  const [loading, setLoading] = useState(false);
  const [models, setModels] = useState<string[]>([]);
  const [selectedModel, setSelectedModel] = useState('');

  // Load models on mount
  useEffect(() => {
    if (isOverallReady && isAuthenticated) {
      loadModels();
    }
  }, [isOverallReady, isAuthenticated]);

  const loadModels = async () => {
    const modelList: string[] = [];
    for await (const model of client.models.list()) {
      modelList.push(model.id);
    }
    setModels(modelList);
    if (modelList.length > 0) setSelectedModel(modelList[0]);
  };

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!prompt.trim() || !selectedModel) return;

    setLoading(true);
    setResponse('');

    try {
      const stream = client.chat.completions.create({
        model: selectedModel,
        messages: [{ role: 'user', content: prompt }],
        stream: true,
      });

      for await (const chunk of stream) {
        const content = chunk.choices?.[0]?.delta?.content || '';
        setResponse(prev => prev + content);
      }
    } catch (err) {
      setResponse(`Error: ${err instanceof Error ? err.message : String(err)}`);
    } finally {
      setLoading(false);
    }
  };

  if (!isOverallReady) {
    return <button onClick={showSetup}>Open Setup</​button>;
  }

  if (!isAuthenticated) {
    return <button onClick={login}>Login</​button>;
  }

  return (
    <div>
      <select value={selectedModel} onChange={e => setSelectedModel(e.target.value)}>
        {models.map(model => (
          <option key={model} value={model}>
            {model}
          </​option>
        ))}
      </​select>
      <form onSubmit={handleSubmit}>
        <input value={prompt} onChange={e => setPrompt(e.target.value)} />
        <button type="submit" disabled={loading}>
          {loading ? 'Generating...' : 'Send'}
        </​button>
      </​form>
      {response && <div>{response}</​div>}
    </​div>
  );
}

export default Chat;

Key APIs and Hooks

useBodhi() Hook

const {
  client, // SDK client instance (OpenAI-compatible API)
  isOverallReady, // Both client AND server ready (most common check)
  isAuthenticated, // User has valid OAuth token
  login, // Initiate OAuth login flow
  logout, // Logout and clear tokens
  showSetup, // Open setup wizard modal

  // Additional properties
  isReady, // Client initialized (extension or direct URL)
  isServerReady, // Server status is 'ready'
  isInitializing, // client.init() in progress
  isExtension, // Using extension mode
  isDirect, // Using direct HTTP mode
  canLogin, // isReady && !isAuthLoading
  isAuthLoading, // Auth operation in progress
} = useBodhi();

Client Methods (OpenAI-Compatible)

// List models (AsyncGenerator)
for await (const model of client.models.list()) {
  console.log(model.id);
}

// Streaming chat
const stream = client.chat.completions.create({
  model: 'gemma-3n-e4b-it',
  messages: [{ role: 'user', content: 'Hello!' }],
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices?.[0]?.delta?.content || '';
  // Append to response
}

// Non-streaming chat
const response = await client.chat.completions.create({
  model: 'gemma-3n-e4b-it',
  messages: [{ role: 'user', content: 'Hello!' }],
  stream: false,
});

Advanced Configuration

Custom Client Config

<BodhiProvider
  authClientId={CLIENT_ID}
  clientConfig={{
    redirectUri: 'https://myapp.com/​callback',
    basePath: '/​app',
    logLevel: 'debug',
  }}
>
  <App />
</​BodhiProvider>

basePath for Sub-paths or GitHub Pages

When your app runs on a sub-path (e.g., GitHub Pages at /​repo-name/):

// Vite config
export default defineConfig({
  base: '/​repo-name/',
});

// BodhiProvider
<BodhiProvider authClientId={CLIENT_ID} basePath="/​repo-name" callbackPath="/​repo-name/​callback">
  <App />
</​BodhiProvider>;

Common Patterns

Conditional Rendering

function App() {
  const { isOverallReady, isAuthenticated, showSetup, login } = useBodhi();

  if (!isOverallReady) {
    return <button onClick={showSetup}>Setup Required</​button>;
  }

  if (!isAuthenticated) {
    return <button onClick={login}>Login Required</​button>;
  }

  return <ChatInterface />;
}

Model Loading with Caching

const loadModels = async () => {
  const cached = localStorage.getItem('bodhi_models');
  if (cached) {
    const { models: cachedModels, expiry } = JSON.parse(cached);
    if (Date.now() < expiry) {
      setModels(cachedModels);
      return;
    }
  }

  const modelList: string[] = [];
  for await (const model of client.models.list()) {
    modelList.push(model.id);
  }
  setModels(modelList);

  localStorage.setItem(
    'bodhi_models',
    JSON.stringify({
      models: modelList,
      expiry: Date.now() + 3600000, // 1 hour
    })
  );
};

Error Handling

try {
  const stream = client.chat.completions.create({ ... });
  for await (const chunk of stream) {
    // Process chunk
  }
} catch (err) {
  if (err instanceof Error) {
    console.error('Chat error:', err.message);
    setError(err.message);
  }
}

Detailed Guides

For comprehensive information on specific topics, see the supporting documentation:

SDK Documentation Reference

The bodhi-js-sdk repository contains comprehensive documentation:

  • bodhi-js-sdk/​docs/​quick-start.md - Official quick start
  • bodhi-js-sdk/​docs/​react-integration.md - Deep dive into React integration
  • bodhi-js-sdk/​docs/​authentication.md - OAuth flow details
  • bodhi-js-sdk/​docs/​streaming.md - Streaming patterns
  • bodhi-js-sdk/​docs/​api-reference.md - Complete API documentation

Implementation Approach

When user asks to integrate bodhi-js-sdk:

  1. Check existing setup: Look for package.json, existing React components
  2. Install package: Run npm install @bodhiapp/​bodhi-js-react
  3. Add BodhiProvider: Wrap root component with provider
  4. Create/​update components: Add useBodhi() hook to components
  5. Test connection: Verify extension detection or direct mode
  6. Add OAuth: Guide user to register at developer.getbodhi.app
  7. Implement chat: Create streaming chat interface
  8. Handle errors: Add proper error boundaries and user feedback

When troubleshooting:

  1. Check connection status: Use isOverallReady, isReady, isServerReady
  2. Verify auth state: Check isAuthenticated, auth object
  3. Inspect logs: Look for [Bodhi/​Web] prefixed logs in console
  4. Review config: Verify authClientId, redirectUri, basePath
  5. Test backend: Ensure local server at http://localhost:1135
  6. Check extension: Verify Bodhi Browser extension installed

Testing Integration

After integration, verify:

  1. Extension detection: Check console for [Bodhi/​Web] Extension detected
  2. Server connection: Verify [Bodhi/​Web] Server ready
  3. Setup flow: Click "Open Setup" to test modal
  4. Authentication: Click "Login" to test OAuth flow
  5. Model loading: Verify models populate in dropdown
  6. Streaming: Send message and verify real-time response streaming
  7. Error handling: Test with server offline to verify error states

Common Integration Tasks

When user requests:

  • "Add bodhi to my React app" → Follow basic integration steps
  • "Setup OAuth for bodhi" → Guide to oauth-setup.md, developer.getbodhi.app
  • "Deploy to GitHub Pages" → Reference github-pages.md for basePath and 404 hack
  • "Chat not working" → Check troubleshooting.md for connection/​auth issues
  • "Models not loading" → Verify server, check models endpoint, review async iteration
  • "Streaming broken" → Verify stream:true, check AsyncGenerator pattern

Key Files to Reference

When implementing integration:

  • bodhi-js-sdk/​docs/​quick-start.md - Primary integration guide
  • bodhi-js-sdk/​docs/​react-integration.md - React-specific patterns
  • bodhi-js-sdk/​docs/ - Comprehensive documentation and examples

Notes

  • Focus on React+Vite projects only
  • Always use @bodhiapp/​bodhi-js-react preset package (simplest)
  • Auto-callback handling is enabled by default (handleCallback={true})
  • OAuth callback happens automatically without custom routes
  • Default backend: http://localhost:1135
  • Extension mode is preferred over direct mode
  • AsyncGenerator pattern for streaming (OpenAI-compatible)

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View