Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@syncagent/react

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@syncagent/react

SyncAgent React SDK — AI database chat widget & hooks

latest
Source
npmnpm
Version
0.4.0
Version published
Weekly downloads
197
1131.25%
Maintainers
1
Weekly downloads
 
Created
Source

@syncagent/react

React SDK for SyncAgent — drop-in AI database chat widget and hooks for React apps.

Works with MongoDB, PostgreSQL, MySQL, SQLite, SQL Server, and Supabase.

npm version License: MIT

Get Your API Key

  • Sign up for a free account
  • Go to your DashboardNew Project → choose your database type
  • Copy your API key (starts with sa_)

Every new project gets a 14-day trial with 500 free requests — no credit card required. After the trial, you get 100 free requests/month on the Free plan.

Install

npm install @syncagent/react @syncagent/js

Quick Start

import { SyncAgentChat } from "@syncagent/react";

export default function App() {
  return (
    <SyncAgentChat
      config={{
        apiKey: "sa_your_api_key",
        connectionString: process.env.DATABASE_URL,
      }}
    />
  );
}

A floating chat button appears in the bottom-right corner. Your users can now query your database in plain English.

<SyncAgentChat> Props

PropTypeDefaultDescription
configSyncAgentConfigRequired*API key, connection string, tools, filter, operations
mode"floating" | "inline""floating"Floating FAB or embedded inline panel
position"bottom-right" | "bottom-left""bottom-right"FAB position (floating mode only)
defaultOpenbooleanfalseStart with the panel open
titlestring"SyncAgent"Header title
subtitlestring"AI Database Assistant"Header subtitle
placeholderstring"Ask anything..."Input placeholder
welcomeMessagestring"Hi! I can query..."Empty state message
accentColorstring"#10b981"Brand color for header, FAB, send button
suggestionsstring[]3 defaultsQuick-start suggestion chips
persistKeystringlocalStorage key for conversation persistence
contextRecord<string, any>Extra context injected into every message
filterRecord<string, any>Mandatory query filter for multi-tenancy
operations("read"|"create"|"update"|"delete")[]Restrict operations for this session
onReaction(idx, reaction, content) => voidCalled when user reacts 👍/👎
onData(data: ToolData) => voidCalled when a DB tool returns structured data

*config is required unless wrapped in <SyncAgentProvider>.

Inline Mode

Embed the chat inside your layout instead of a floating button:

<div style={{ height: 600 }}>
  <SyncAgentChat
    config={{ apiKey: "...", connectionString: "..." }}
    mode="inline"
  />
</div>

Custom UI with useSyncAgent

Build your own chat UI with full control:

import { SyncAgentProvider, useSyncAgent } from "@syncagent/react";

export default function App() {
  return (
    <SyncAgentProvider config={{ apiKey: "...", connectionString: "..." }}>
      <MyChat />
    </SyncAgentProvider>
  );
}

function MyChat() {
  const { messages, isLoading, error, status, lastData, sendMessage, stop, reset } = useSyncAgent();

  return (
    <div>
      {status && <div>⏳ {status.label}</div>}
      {messages.map((msg, i) => (
        <div key={i}><strong>{msg.role}:</strong> {msg.content}</div>
      ))}
      <button onClick={() => sendMessage("Show all users")}>Ask</button>
      <button onClick={stop}>Stop</button>
      <button onClick={reset}>Clear</button>
    </div>
  );
}

useSyncAgent Returns

ReturnTypeDescription
messagesMessage[]Full conversation history
isLoadingbooleantrue while streaming
errorError | nullLast error
status{ step, label } | nullLive status while agent is working
lastDataToolData | nullLast structured data from a DB tool
sendMessage(content: string) => voidSend a user message
stop() => voidAbort the current stream
reset() => voidClear all messages

Features

  • Auto page detection — detects current page, record ID, and query params from the URL
  • Live status — shows ● Querying users... while the agent works
  • Markdown rendering — tables, code blocks, bold, italic, lists
  • Streaming — blinking cursor while text streams in
  • Copy button — on every AI response
  • Reactions — 👍/👎 on AI messages
  • Conversation persistence — saves history to localStorage
  • Suggestion chips — configurable quick-start prompts
  • Export CSV — download tables as CSV
  • Bar charts — auto-renders aggregation results
  • Resize handle — drag to resize the floating panel
  • Mobile responsive — full-width on small screens
  • Dark mode — respects prefers-color-scheme

Multi-tenant SaaS

Pass filter to scope every agent operation to the current user's organization. Enforced server-side.

<SyncAgentChat
  config={{
    apiKey: "sa_your_key",
    connectionString: process.env.DATABASE_URL,
    filter: { organizationId: currentUser.orgId },
    operations: currentUser.isAdmin
      ? ["read", "create", "update", "delete"]
      : ["read"],
  }}
/>

Custom Tools

Give the agent capabilities beyond your database:

<SyncAgentChat
  config={{
    apiKey: "sa_your_key",
    connectionString: process.env.DATABASE_URL,
    tools: {
      createInvoice: {
        description: "Create a Stripe invoice for a customer",
        inputSchema: {
          customerId: { type: "string", description: "Stripe customer ID" },
          amount:     { type: "number", description: "Amount in cents" },
        },
        execute: async ({ customerId, amount }) => {
          const inv = await stripe.invoices.create({ customer: customerId });
          return { invoiceId: inv.id };
        },
      },
    },
  }}
/>

Tools-only Mode

Use the agent with only your custom tools — no database access:

<SyncAgentChat
  config={{
    apiKey: "sa_your_key",
    toolsOnly: true,
    tools: {
      searchProducts: {
        description: "Search products by name",
        inputSchema: { query: { type: "string", description: "Search query" } },
        execute: async ({ query }) => {
          const res = await fetch(`/api/products?q=${query}`);
          return res.json();
        },
      },
    },
  }}
/>

Customer Agent Mode

Use the useCustomerChat hook to build customer-facing support interfaces powered by SyncAgent's customer agent pipeline — including persona, flows, knowledge base, escalation, and AI fallback.

import { SyncAgentProvider, useCustomerChat } from "@syncagent/react";

function App() {
  return (
    <SyncAgentProvider
      config={{
        apiKey: "sa_your_api_key",
        connectionString: process.env.DATABASE_URL,
        externalUserId: currentUser.id,
      }}
    >
      <CustomerChat />
    </SyncAgentProvider>
  );
}

function CustomerChat() {
  const {
    messages,
    conversationId,
    isLoading,
    isEscalated,
    isResolved,
    error,
    welcomeMessage,
    sendMessage,
    rateConversation,
    reset,
  } = useCustomerChat({
    onEscalated: () => console.log("Escalated to human agent"),
    onResolved: (id) => console.log("Conversation resolved:", id),
  });

  return (
    <div>
      {welcomeMessage && <p>{welcomeMessage}</p>}
      {messages.map((msg, i) => (
        <div key={i}>
          <strong>{msg.role}:</strong> {msg.content}
        </div>
      ))}
      {isEscalated && <p>You've been connected to a human agent.</p>}
      {isResolved && (
        <div>
          <p>Conversation resolved!</p>
          <button onClick={() => rateConversation(5)}>⭐ Rate 5/5</button>
        </div>
      )}
      {error && <p>Error: {error.message}</p>}
      <button onClick={() => sendMessage("I need help with my order")}>
        Send
      </button>
      <button onClick={reset}>New Conversation</button>
    </div>
  );
}

useCustomerChat Return Values

ReturnTypeDescription
messagesMessage[]Full conversation history (user and assistant messages)
conversationIdstring | nullCurrent conversation ID, set after first message
isLoadingbooleantrue while waiting for a response
isEscalatedbooleantrue when conversation has been escalated to a human agent
isResolvedbooleantrue when the conversation has been resolved
errorError | nullLast error encountered, or null
welcomeMessagestring | nullWelcome message returned on first interaction
sendMessage(content: string, metadata?: Record<string, any>) => Promise<void>Send a message to the customer agent
rateConversation(rating: number) => Promise<void>Rate the conversation (1-5). Throws if no active conversation.
reset() => voidClear all state and start a new conversation

UseCustomerChatOptions

FieldTypeDescription
clientSyncAgentClient?Optional client instance. If omitted, uses the client from SyncAgentProvider context.
onEscalated() => void?Called when the conversation is escalated to a human agent
onResolved(conversationId: string) => void?Called when the conversation is resolved

Client resolution: useCustomerChat first checks for a client passed directly in options. If none is provided, it falls back to the client from the nearest <SyncAgentProvider>. If neither is available, it throws an error prompting you to provide one.

Guest Identification

When no externalUserId is provided in the config, the customer chat enters guest mode — anonymous visitors must identify themselves before sending messages. The useCustomerChat hook exposes guest identification state and a method to submit guest data programmatically.

Hook Fields

ReturnTypeDescription
isIdentifiedbooleantrue if the user has an externalUserId or has completed guest identification
guestIdentityGuestIdentity | nullThe stored guest identity object, or null if not yet identified
identifyGuest(data: { name: string; email: string; phone?: string }) => voidSubmit guest form data — validates, generates a guest ID, persists to localStorage, and transitions to identified state

Using GuestIdentificationForm

The GuestIdentificationForm component provides a ready-made form with built-in validation, accessibility attributes, and customizable text:

import { useCustomerChat, GuestIdentificationForm } from "@syncagent/react";

function CustomerChat() {
  const {
    messages,
    isIdentified,
    identifyGuest,
    sendMessage,
  } = useCustomerChat({
    onGuestIdentified: (identity) => {
      console.log("Guest identified:", identity.guestId);
    },
  });

  if (!isIdentified) {
    return (
      <GuestIdentificationForm
        onSubmit={(identity) => identifyGuest(identity)}
        config={{
          title: "Welcome!",
          subtitle: "Tell us a bit about yourself",
          submitButtonText: "Start Chat",
          namePlaceholder: "Your name",
          emailPlaceholder: "you@example.com",
          phonePlaceholder: "Phone (optional)",
        }}
        className="my-guest-form"
      />
    );
  }

  return (
    <div>
      {messages.map((msg, i) => (
        <div key={i}><strong>{msg.role}:</strong> {msg.content}</div>
      ))}
      <button onClick={() => sendMessage("Hello!")}>Send</button>
    </div>
  );
}

GuestIdentificationFormProps

PropTypeRequiredDescription
onSubmit(identity: GuestIdentity) => voidYesCalled with the complete GuestIdentity (including generated guestId) on valid submission
configGuestFormConfigNoCustomize title, subtitle, button text, and placeholder strings
classNamestringNoCustom CSS class applied to the form container

Manual Guest Identification

If you prefer to build your own form UI, use the identifyGuest method directly:

import { useState } from "react";
import { useCustomerChat } from "@syncagent/react";

function CustomGuestForm() {
  const { isIdentified, identifyGuest, error } = useCustomerChat();
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");

  if (isIdentified) return null;

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        identifyGuest({ name, email });
      }}
    >
      <input value={name} onChange={(e) => setName(e.target.value)} placeholder="Name" />
      <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" />
      <button type="submit">Continue</button>
      {error && <p>{error.message}</p>}
    </form>
  );
}

The identifyGuest method validates the input using validateGuestForm from @syncagent/js. If validation fails, it sets the error state with a descriptive message. On success, it generates a deterministic guest identifier from the email, persists the identity to localStorage, and sets isIdentified to true.

Dual Mode (Database + Customer Agent)

⚠️ Deprecated: createDual() will be removed in a future major version. Use useDualChat() instead — pass externalUserId directly to enable both modes on a single instance.

Before (deprecated):

import { SyncAgentClient } from "@syncagent/js";
import { SyncAgentProvider, useCustomerChat, useSyncAgent } from "@syncagent/react";

const { db, support } = SyncAgentClient.createDual({
  apiKey: "sa_your_key",
  connectionString: process.env.DATABASE_URL,
  externalUserId: currentUser.id,
});

function AdminChat() {
  return <SyncAgentProvider config={db}><AdminPanel /></SyncAgentProvider>;
}

function CustomerWidget() {
  const chat = useCustomerChat({ client: support });
  return <div>{/* customer chat UI */}</div>;
}

After (recommended):

import { SyncAgentProvider, useDualChat } from "@syncagent/react";

function App() {
  return (
    <SyncAgentProvider
      config={{
        apiKey: "sa_your_key",
        connectionString: process.env.DATABASE_URL,
        externalUserId: currentUser.id,
      }}
    >
      <DualChatUI />
    </SyncAgentProvider>
  );
}

function DualChatUI() {
  const { db, support } = useDualChat();
  // Use db.sendMessage(), support.sendMessage(), etc.
}

Legacy pattern (deprecated):

import { SyncAgentClient } from "@syncagent/js";
import { SyncAgentProvider, useCustomerChat, useSyncAgent } from "@syncagent/react";

const { db, support } = SyncAgentClient.createDual({
  apiKey: "sa_your_key",
  connectionString: process.env.DATABASE_URL,
  externalUserId: currentUser.id,
});

// Admin panel — database agent
function AdminChat() {
  return <SyncAgentProvider config={db}><AdminPanel /></SyncAgentProvider>;
}

// Customer widget — support agent
function CustomerWidget() {
  const chat = useCustomerChat({ client: support });
  return <div>{/* customer chat UI */}</div>;
}

Unified Dual Mode

The useDualChat() hook provides a single interface for managing both database agent and customer support agent conversations from within a <SyncAgentProvider>. When your config includes externalUserId, both modes are available through namespaced db and support objects.

import { SyncAgentProvider, useDualChat } from "@syncagent/react";

function App() {
  return (
    <SyncAgentProvider
      config={{
        apiKey: "sa_your_api_key",
        connectionString: process.env.DATABASE_URL,
        externalUserId: currentUser.id,
      }}
    >
      <DualChatUI />
    </SyncAgentProvider>
  );
}

function DualChatUI() {
  const { db, support } = useDualChat({
    context: { tenant: currentUser.orgId },
    onData: (data) => console.log("DB tool result:", data),
    onEscalated: () => console.log("Escalated to human"),
    onResolved: (id) => console.log("Resolved:", id),
  });

  return (
    <div>
      {/* Database agent panel */}
      <section>
        <h2>Admin Database Chat</h2>
        {db.messages.map((msg, i) => (
          <div key={i}><strong>{msg.role}:</strong> {msg.content}</div>
        ))}
        {db.isLoading && <p>Loading...</p>}
        {db.error && <p>Error: {db.error.message}</p>}
        <button onClick={() => db.sendMessage("Show all orders")}>
          Query DB
        </button>
        <button onClick={db.stop}>Stop</button>
        <button onClick={db.reset}>Clear</button>
      </section>

      {/* Customer support panel */}
      <section>
        <h2>Customer Support</h2>
        {support.welcomeMessage && <p>{support.welcomeMessage}</p>}
        {support.messages.map((msg, i) => (
          <div key={i}><strong>{msg.role}:</strong> {msg.content}</div>
        ))}
        {support.isLoading && <p>Loading...</p>}
        {support.isEscalated && <p>Connected to a human agent.</p>}
        {support.isResolved && (
          <button onClick={() => support.rateConversation(5)}>⭐ Rate</button>
        )}
        {support.error && <p>Error: {support.error.message}</p>}
        <button onClick={() => support.sendMessage("I need help")}>
          Send
        </button>
        <button onClick={support.reset}>New Conversation</button>
      </section>
    </div>
  );
}

Error Handling

If useDualChat() is called outside of a <SyncAgentProvider>, it throws:

useDualChat must be used within a <SyncAgentProvider>

Ensure your component is wrapped in a <SyncAgentProvider> with a valid config that includes externalUserId.

DualChatReturn

The useDualChat() hook returns an object with two namespaces: db for database agent state and support for customer agent state.

db Properties

PropertyTypeDescription
messagesMessage[]Full database agent conversation history
isLoadingbooleantrue while the database agent is streaming
errorError | nullLast error from the database agent, or null
status{ step: string; label: string } | nullLive status while the agent is working (e.g., querying)
lastDataToolData | nullLast structured data returned by a DB tool
sendMessage(content: string) => voidSend a message to the database agent
stop() => voidAbort the current database agent stream
reset() => voidClear all database agent messages and state

support Properties

PropertyTypeDescription
messagesMessage[]Full customer support conversation history
conversationIdstring | nullCurrent conversation ID, set after first message
isLoadingbooleantrue while waiting for a support response
isEscalatedbooleantrue when conversation has been escalated to a human agent
isResolvedbooleantrue when the conversation has been resolved
errorError | nullLast error from the support agent, or null
welcomeMessagestring | nullWelcome message returned on first interaction
sendMessage(content: string, metadata?: Record<string, any>) => Promise<void>Send a message to the customer support agent
rateConversation(rating: number) => Promise<void>Rate the conversation (1-5). Throws if no active conversation.
reset() => voidClear all support state and start a new conversation

UseDualChatOptions

OptionTypeRequiredDescription
contextRecord<string, any>NoExtra context injected into every database agent message
onData(data: ToolData) => voidNoCalled when a DB tool returns structured data
onEscalated() => voidNoCalled when the customer conversation is escalated to a human agent
onResolved(conversationId: string) => voidNoCalled when the customer conversation is resolved

Customization Options

All config options from @syncagent/js work here too:

<SyncAgentChat
  config={{
    apiKey: "sa_your_key",
    connectionString: process.env.DATABASE_URL,
    systemInstruction: "You are a friendly sales assistant for Acme Corp.",
    language: "French",
    confirmWrites: true,
    maxResults: 10,
    sensitiveFields: ["ssn", "salary", "creditCard"],
    onBeforeToolCall: (name, args) => { console.log(`[Audit] ${name}`, args); return true; },
    onAfterToolCall: (name, args, result) => { analytics.track("tool_call", { tool: name }); },
  }}
/>

Conversation Persistence

<SyncAgentChat
  config={{ apiKey: "...", connectionString: "..." }}
  persistKey={currentUser.id}
/>

History saves to localStorage under sa_chat_{persistKey}. The "New" button clears it.

Context & Auto Page Detection

The SDK automatically detects the current page from window.location — zero config needed:

URL: /dashboard/orders/ord_123?tab=details

Auto-detected:
  currentPage: "orders"
  currentPath: "/dashboard/orders/ord_123"
  currentRecordId: "ord_123"
  param_tab: "details"

Pass additional context:

<SyncAgentChat
  config={{ apiKey: "...", connectionString: "..." }}
  context={{ userId: currentUser.id, userRole: "admin" }}
/>

Vanilla JS Widget

No npm required — drop a script tag into any HTML page:

<script src="https://syncagentdev.vercel.app/api/v1/widget"></script>
<script>
  SyncAgent.init({
    apiKey: "sa_your_key",
    connectionString: "your_database_url",
    position: "right",
    accentColor: "#10b981",
    title: "AI Assistant",
    persistKey: "my-app",
  });
</script>

Security

  • Your database connection string is never stored on SyncAgent servers
  • Passed at runtime, used once, immediately discarded
  • API keys are hashed with bcrypt
  • Never expose your connection string in client-side code — use server components or API routes

Plans & Pricing

PlanRequests/moCollectionsPrice
Free (+ 14-day trial)100 (500 during trial)5GH₵0
Starter5,00020GH₵150/mo
Pro50,000UnlimitedGH₵500/mo
EnterpriseUnlimitedUnlimitedCustom

View full pricing →

Resources

TypeScript Types

The following types are exported from @syncagent/react for use in your TypeScript projects:

TypeDescriptionImport
DualChatReturnReturn type of the useDualChat() hook containing namespaced db and support objectsimport { DualChatReturn } from "@syncagent/react"
UseDualChatOptionsOptions interface for configuring the useDualChat() hook (context, callbacks)import { UseDualChatOptions } from "@syncagent/react"
import { DualChatReturn, UseDualChatOptions } from "@syncagent/react";

License

MIT

Keywords

syncagent

FAQs

Package last updated on 21 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts