Skip to main content

React Hooks

The Raptor SDK includes React hooks for managing document processing state, auto-link settings, and real-time updates in your React applications.
The SDK must be used server-side only. Never expose your API key in client-side code. Use React hooks in Server Components or with server-side API routes.

Installation

The React hooks are included with the SDK:
npm install @raptor-data/ts-sdk

Available Hooks

useProcessingStatus

Monitor document processing status in real-time:
import { useProcessingStatus } from '@raptor-data/ts-sdk/hooks';
import Raptor from '@raptor-data/ts-sdk';

const raptor = new Raptor({ apiKey: process.env.RAPTOR_API_KEY });

function DocumentStatus({ variantId }: { variantId: string }) {
  const { variant, loading, error, refetch } = useProcessingStatus(raptor, variantId, {
    pollInterval: 2000,  // Poll every 2 seconds
    enabled: true        // Auto-start polling
  });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h3>Processing Status</h3>
      <p>Status: {variant?.status}</p>
      <p>Chunks: {variant?.chunksCount || 0}</p>
      <p>Tokens: {variant?.totalTokens || 0}</p>

      {variant?.status === 'processing' && (
        <div>
          <p>Processing...</p>
          <button onClick={refetch}>Check Now</button>
        </div>
      )}

      {variant?.status === 'completed' && (
        <p>✓ Processing complete!</p>
      )}

      {variant?.status === 'failed' && (
        <p>✗ Failed: {variant.error}</p>
      )}
    </div>
  );
}
Options:
pollInterval
number
default:"2000"
Polling interval in milliseconds
enabled
boolean
default:"true"
Whether to start polling automatically
onComplete
function
Callback when processing completes
onError
function
Callback when processing fails

useAutoLinkSettings

Manage auto-linking preferences:
import { useAutoLinkSettings } from '@raptor-data/ts-sdk/hooks';

function AutoLinkSettings() {
  const { settings, loading, error, updateSettings } = useAutoLinkSettings(raptor);

  if (loading) return <p>Loading settings...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h3>Auto-Link Settings</h3>

      <label>
        <input
          type="checkbox"
          checked={settings?.autoLinkEnabled || false}
          onChange={(e) => updateSettings({
            autoLinkEnabled: e.target.checked
          })}
        />
        Enable Auto-Linking
      </label>

      <label>
        Confidence Threshold: {settings?.autoLinkThreshold || 0.85}
        <input
          type="range"
          min="0.5"
          max="1.0"
          step="0.05"
          value={settings?.autoLinkThreshold || 0.85}
          onChange={(e) => updateSettings({
            autoLinkThreshold: parseFloat(e.target.value)
          })}
        />
      </label>

      <p>
        Current threshold: {((settings?.autoLinkThreshold || 0.85) * 100).toFixed(0)}%
      </p>
    </div>
  );
}

useDocumentUpload

Handle file uploads with progress tracking:
import { useDocumentUpload } from '@raptor-data/ts-sdk/hooks';

function DocumentUploader() {
  const { upload, uploading, progress, result, error } = useDocumentUpload(raptor);

  const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    const uploadResult = await upload(file, {
      chunkSize: 512,
      versionLabel: 'New Upload'
    });

    console.log('Upload complete:', uploadResult);
  };

  return (
    <div>
      <input
        type="file"
        onChange={handleFileChange}
        disabled={uploading}
      />

      {uploading && (
        <div>
          <p>Uploading... {progress}%</p>
          <progress value={progress} max={100} />
        </div>
      )}

      {result && (
        <div>
          <p>✓ Upload complete!</p>
          <p>Document ID: {result.documentId}</p>
          <p>Chunks: {result.chunks?.length || 0}</p>

          {result.autoLinked && (
            <p>
              Auto-linked with {(result.autoLinkConfidence * 100).toFixed(0)}% confidence
            </p>
          )}
        </div>
      )}

      {error && (
        <p>✗ Error: {error.message}</p>
      )}
    </div>
  );
}

useDocumentList

Fetch and display user’s documents:
import { useDocumentList } from '@raptor-data/ts-sdk/hooks';

function DocumentList() {
  const { documents, loading, error, refetch, hasMore, loadMore } = useDocumentList(raptor, {
    limit: 20,
    offset: 0
  });

  if (loading) return <p>Loading documents...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h3>Your Documents ({documents.length})</h3>

      <button onClick={refetch}>Refresh</button>

      <ul>
        {documents.map(doc => (
          <li key={doc.id}>
            <strong>{doc.filename}</strong>
            <p>Status: {doc.status}</p>
            <p>Chunks: {doc.chunksCount}</p>
            <p>Uploaded: {new Date(doc.createdAt).toLocaleDateString()}</p>
          </li>
        ))}
      </ul>

      {hasMore && (
        <button onClick={loadMore}>Load More</button>
      )}
    </div>
  );
}

Complete Examples

Document Upload Flow

'use client';

import { useState } from 'react';
import { useDocumentUpload, useProcessingStatus } from '@raptor-data/ts-sdk/hooks';
import Raptor from '@raptor-data/ts-sdk';

// Initialize client (server-side only!)
const raptor = new Raptor({ apiKey: process.env.RAPTOR_API_KEY });

export function DocumentUploadFlow() {
  const [variantId, setVariantId] = useState<string | null>(null);
  const { upload, uploading, error: uploadError } = useDocumentUpload(raptor);
  const { variant, loading } = useProcessingStatus(raptor, variantId, {
    enabled: !!variantId,
    pollInterval: 2000
  });

  const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    const result = await upload(file, {
      wait: false,  // Don't wait for processing
      chunkSize: 512,
      versionLabel: file.name
    });

    if (result) {
      setVariantId(result.variantId);
    }
  };

  return (
    <div>
      <h2>Upload Document</h2>

      <input
        type="file"
        onChange={handleUpload}
        disabled={uploading}
      />

      {uploading && <p>Uploading...</p>}
      {uploadError && <p>Upload error: {uploadError.message}</p>}

      {variantId && (
        <div>
          <h3>Processing Status</h3>

          {loading && <p>Checking status...</p>}

          {variant && (
            <div>
              <p>Status: <strong>{variant.status}</strong></p>

              {variant.status === 'pending' && (
                <p>⏳ Queued for processing...</p>
              )}

              {variant.status === 'processing' && (
                <p>⚙️ Processing document...</p>
              )}

              {variant.status === 'completed' && (
                <div>
                  <p>✓ Processing complete!</p>
                  <p>Chunks: {variant.chunksCount}</p>
                  <p>Tokens: {variant.totalTokens}</p>
                </div>
              )}

              {variant.status === 'failed' && (
                <p>✗ Failed: {variant.error}</p>
              )}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

Settings Panel

'use client';

import { useAutoLinkSettings } from '@raptor-data/ts-sdk/hooks';
import Raptor from '@raptor-data/ts-sdk';

const raptor = new Raptor({ apiKey: process.env.RAPTOR_API_KEY });

export function SettingsPanel() {
  const { settings, loading, error, updateSettings } = useAutoLinkSettings(raptor);

  if (loading) {
    return <div>Loading settings...</div>;
  }

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return (
    <div className="settings-panel">
      <h2>Auto-Link Settings</h2>

      <div className="setting">
        <label>
          <input
            type="checkbox"
            checked={settings?.autoLinkEnabled || false}
            onChange={(e) => updateSettings({
              autoLinkEnabled: e.target.checked
            })}
          />
          <span>Enable automatic version detection</span>
        </label>
        <p className="help-text">
          Automatically detect when you're uploading a new version of an existing document.
        </p>
      </div>

      <div className="setting">
        <label>
          <span>Confidence Threshold</span>
          <input
            type="range"
            min="0.5"
            max="1.0"
            step="0.05"
            value={settings?.autoLinkThreshold || 0.85}
            onChange={(e) => updateSettings({
              autoLinkThreshold: parseFloat(e.target.value)
            })}
            disabled={!settings?.autoLinkEnabled}
          />
          <span className="threshold-value">
            {((settings?.autoLinkThreshold || 0.85) * 100).toFixed(0)}%
          </span>
        </label>
        <p className="help-text">
          Minimum confidence required to auto-link documents. Higher values mean fewer false positives.
        </p>
      </div>

      <div className="recommendation">
        {settings?.autoLinkThreshold >= 0.95 && (
          <p>Very conservative - only links with very high confidence</p>
        )}
        {settings?.autoLinkThreshold >= 0.85 && settings?.autoLinkThreshold < 0.95 && (
          <p>Balanced - recommended for most use cases</p>
        )}
        {settings?.autoLinkThreshold < 0.85 && (
          <p>Aggressive - may link unrelated documents</p>
        )}
      </div>
    </div>
  );
}

Document Browser

'use client';

import { useState } from 'react';
import { useDocumentList } from '@raptor-data/ts-sdk/hooks';
import Raptor from '@raptor-data/ts-sdk';

const raptor = new Raptor({ apiKey: process.env.RAPTOR_API_KEY });

export function DocumentBrowser() {
  const [limit] = useState(20);
  const [offset, setOffset] = useState(0);

  const { documents, loading, error, refetch, hasMore } = useDocumentList(raptor, {
    limit,
    offset
  });

  const handlePrevious = () => {
    setOffset(Math.max(0, offset - limit));
  };

  const handleNext = () => {
    setOffset(offset + limit);
  };

  if (loading) {
    return <div>Loading documents...</div>;
  }

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return (
    <div className="document-browser">
      <div className="header">
        <h2>Documents</h2>
        <button onClick={refetch}>Refresh</button>
      </div>

      <div className="document-grid">
        {documents.map(doc => (
          <div key={doc.id} className="document-card">
            <h3>{doc.filename}</h3>

            <div className="status">
              <span className={`badge ${doc.status}`}>
                {doc.status}
              </span>
            </div>

            <div className="stats">
              <div>
                <strong>Chunks:</strong> {doc.chunksCount}
              </div>
              <div>
                <strong>Tokens:</strong> {doc.totalTokens}
              </div>
              <div>
                <strong>Size:</strong> {(doc.fileSizeBytes / 1024).toFixed(1)} KB
              </div>
            </div>

            <div className="metadata">
              <div>
                <strong>Uploaded:</strong>{' '}
                {new Date(doc.createdAt).toLocaleDateString()}
              </div>
              {doc.versionLabel && (
                <div>
                  <strong>Version:</strong> {doc.versionLabel}
                </div>
              )}
            </div>
          </div>
        ))}
      </div>

      {documents.length === 0 && (
        <div className="empty-state">
          <p>No documents yet. Upload your first document to get started!</p>
        </div>
      )}

      <div className="pagination">
        <button
          onClick={handlePrevious}
          disabled={offset === 0}
        >
          Previous
        </button>

        <span>
          Showing {offset + 1}-{offset + documents.length}
        </span>

        <button
          onClick={handleNext}
          disabled={!hasMore}
        >
          Next
        </button>
      </div>
    </div>
  );
}

Server-Side Usage

Next.js Server Component

// app/documents/page.tsx
import Raptor from '@raptor-data/ts-sdk';

const raptor = new Raptor({ apiKey: process.env.RAPTOR_API_KEY! });

export default async function DocumentsPage() {
  const documents = await raptor.listDocuments({ limit: 20 });

  return (
    <div>
      <h1>Documents</h1>
      <ul>
        {documents.map(doc => (
          <li key={doc.id}>{doc.filename}</li>
        ))}
      </ul>
    </div>
  );
}

Next.js API Route with Hook

// app/api/upload/route.ts
import Raptor from '@raptor-data/ts-sdk';
import { NextRequest } from 'next/server';

const raptor = new Raptor({ apiKey: process.env.RAPTOR_API_KEY! });

export async function POST(request: NextRequest) {
  const formData = await request.formData();
  const file = formData.get('file') as File;

  const result = await raptor.process(file, {
    wait: true
  });

  return Response.json({
    documentId: result.documentId,
    chunks: result.chunks.length
  });
}

Best Practices

Server-side only: Never expose your API key in client-side code. Use Server Components or API routes.
Polling efficiently: Set appropriate poll intervals (2-5 seconds) to balance responsiveness and API usage.
Error handling: Always handle errors from hooks to provide good user experience.