
Research
/Security News
Coruna Respawned: Compromised art-template npm Package Leads to iOS Browser Exploit Kit
Compromised npm package art-template delivered a Coruna-like iOS Safari exploit framework through a watering-hole attack.
@syncagent/nextjs
Advanced tools
Next.js SDK for SyncAgent — server-safe helpers that keep your database connection string out of the browser bundle.
Works with MongoDB, PostgreSQL, MySQL, SQLite, SQL Server, and Supabase.
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.
npm install @syncagent/nextjs @syncagent/js @syncagent/react
The @syncagent/nextjs package provides createServerConfig() which lets you safely pass your database connection string from Server Components — it never reaches the browser bundle. All config options from @syncagent/js are supported.
// app/dashboard/page.tsx — Server Component (no "use client")
import { createServerConfig } from "@syncagent/nextjs/server";
import { SyncAgentChat } from "@syncagent/nextjs";
import { getServerSession } from "next-auth";
export default async function DashboardPage() {
const session = await getServerSession();
const config = createServerConfig({
apiKey: process.env.SYNCAGENT_KEY!,
connectionString: process.env.DATABASE_URL!,
filter: { organizationId: session.user.orgId },
operations: session.user.isAdmin
? ["read", "create", "update", "delete"]
: ["read"],
});
return (
<SyncAgentChat
config={config}
persistKey={session.user.id}
accentColor="#6366f1"
context={{ userId: session.user.id, page: "dashboard" }}
/>
);
}
import { createServerConfigFromEnv } from "@syncagent/nextjs/server";
// Reads SYNCAGENT_KEY and DATABASE_URL automatically
const config = createServerConfigFromEnv({
filter: { orgId: session.user.orgId },
language: "French",
});
# .env.local
SYNCAGENT_KEY=sa_your_api_key
DATABASE_URL=mongodb+srv://user:pass@cluster/db
createServerConfig accepts all SyncAgentConfig options:
| Option | Type | Required | Description |
|---|---|---|---|
apiKey | string | ✅ | Your SyncAgent API key |
connectionString | string | ✅* | Your database URL — stays on the server. *Optional when toolsOnly: true. |
filter | Record<string,any> | — | Mandatory query filter for multi-tenancy |
operations | string[] | — | Restrict operations for this session |
toolsOnly | boolean | — | Disables all DB tools — agent only uses custom tools |
systemInstruction | string | — | Custom agent instructions — personality, tone, rules |
confirmWrites | boolean | — | Ask for confirmation before writes |
language | string | — | Language the agent responds in (e.g. "French") |
maxResults | number | — | Default max records per query (default 50) |
sensitiveFields | string[] | — | Fields to mask in responses |
autoDetectPage | boolean | — | Auto-detect page from URL (default true) |
baseUrl | string | — | Override API URL (dev only) |
// app/app/[orgSlug]/page.tsx — Server Component
import { createServerConfig } from "@syncagent/nextjs/server";
import { SyncAgentChat } from "@syncagent/nextjs";
import { getSession } from "@/lib/auth";
import { getOrganization } from "@/lib/db";
export default async function AppPage({ params }: { params: { orgSlug: string } }) {
const session = await getSession();
const org = await getOrganization(params.orgSlug);
const config = createServerConfig({
apiKey: process.env.SYNCAGENT_KEY!,
connectionString: process.env.DATABASE_URL!,
filter: { organizationId: org.id },
operations: session.user.role === "admin"
? ["read", "create", "update", "delete"]
: ["read"],
systemInstruction: `You are the AI assistant for ${org.name}. Be professional and helpful.`,
language: org.language || "English",
confirmWrites: true,
maxResults: 25,
sensitiveFields: ["ssn", "salary", "password"],
});
return (
<SyncAgentChat
config={config}
persistKey={`${org.id}-${session.user.id}`}
title={`${org.name} AI`}
accentColor={org.brandColor}
context={{ userId: session.user.id, userRole: session.user.role, orgName: org.name }}
/>
);
}
// Server Component
import { createServerConfig } from "@syncagent/nextjs/server";
import { ChatWithTools } from "./chat-with-tools";
export default async function Page() {
const config = createServerConfig({
apiKey: process.env.SYNCAGENT_KEY!,
toolsOnly: true,
systemInstruction: "You are a product search assistant.",
});
return <ChatWithTools serverConfig={config} />;
}
// app/dashboard/chat-with-tools.tsx — "use client"
import { SyncAgentChat } from "@syncagent/nextjs";
export function ChatWithTools({ serverConfig }) {
return (
<SyncAgentChat
config={{
...serverConfig,
tools: {
searchProducts: {
description: "Search products",
inputSchema: { query: { type: "string" } },
execute: async ({ query }) => {
const res = await fetch(`/api/products?q=${query}`);
return res.json();
},
},
},
}}
/>
);
}
The createCustomerServerConfig server helper configures customer agent mode from a Server Component. It validates that externalUserId is provided — customer mode is automatically enabled when externalUserId is present.
// app/support/page.tsx — Server Component
import { createCustomerServerConfig } from "@syncagent/nextjs/server";
import { getServerSession } from "next-auth";
import { CustomerSupportWidget } from "./customer-support-widget";
export default async function SupportPage() {
const session = await getServerSession();
const config = createCustomerServerConfig({
apiKey: process.env.SYNCAGENT_KEY!,
connectionString: process.env.DATABASE_URL!,
externalUserId: session.user.id,
filter: { organizationId: session.user.orgId },
});
return <CustomerSupportWidget config={config} />;
}
// app/support/customer-support-widget.tsx — "use client"
"use client";
import { useCustomerChat } from "@syncagent/nextjs";
import { SyncAgentClient } from "@syncagent/nextjs";
import type { SyncAgentConfig } from "@syncagent/js";
export function CustomerSupportWidget({ config }: { config: SyncAgentConfig }) {
const client = new SyncAgentClient(config);
const {
messages,
sendMessage,
isLoading,
isEscalated,
isResolved,
welcomeMessage,
rateConversation,
} = useCustomerChat({ client });
return (
<div>
{welcomeMessage && <p>{welcomeMessage}</p>}
{messages.map((msg, i) => (
<div key={i}>{msg.role}: {msg.content}</div>
))}
{isEscalated && <p>You have been connected to a human agent.</p>}
{isResolved && (
<div>
<p>Conversation resolved.</p>
<button onClick={() => rateConversation(5)}>Rate 5 ⭐</button>
</div>
)}
<input
onKeyDown={(e) => {
if (e.key === "Enter") sendMessage(e.currentTarget.value);
}}
disabled={isLoading}
/>
</div>
);
}
externalUserId RequirementThe externalUserId option is required when using createCustomerServerConfig. Omitting it throws an error:
Error: @syncagent/nextjs: externalUserId is required for customer mode
This ID scopes conversations to a specific customer and should always come from an authenticated session (e.g. session.user.id).
useCustomerChat is re-exported from @syncagent/nextjs for convenience — no need to install @syncagent/react separately:
import { useCustomerChat } from "@syncagent/nextjs";
The GuestIdentificationForm component and all guest identification utilities are re-exported from @syncagent/nextjs — no need to install @syncagent/react or @syncagent/js separately.
Available exports:
| Export | Kind | Description |
|---|---|---|
GuestIdentificationForm | Component | Pre-built form for collecting guest name, email, and optional phone |
GuestIdentificationFormProps | Type | Props interface for the form component |
GuestIdentity | Type | Object containing name, email, phone, and generated guestId |
GuestFormConfig | Type | Configuration for customizing form text and placeholders |
FieldValidationResult | Type | Validation result for individual form fields |
validateGuestForm | Function | Validates all guest form fields at once |
validateName | Function | Validates a name string (non-empty, non-whitespace) |
validateEmail | Function | Validates an email string |
generateGuestIdentifier | Function | Generates a deterministic guest_-prefixed ID from an email |
GuestIdentificationRequiredError | Class | Error thrown when sending a message before identification |
Important: The
GuestIdentificationFormis a client component. You must add the"use client"directive at the top of any file that uses it in the Next.js App Router.
// app/support/guest-chat.tsx
"use client";
import {
useCustomerChat,
GuestIdentificationForm,
SyncAgentClient,
} from "@syncagent/nextjs";
import type { SyncAgentConfig, GuestIdentity } from "@syncagent/nextjs";
export function GuestChat({ config }: { config: SyncAgentConfig }) {
const client = new SyncAgentClient(config);
const {
messages,
sendMessage,
isLoading,
isIdentified,
guestIdentity,
identifyGuest,
} = useCustomerChat({
client,
onGuestIdentified: (identity: GuestIdentity) => {
console.log("Guest identified:", identity.guestId);
},
});
if (!isIdentified) {
return (
<GuestIdentificationForm
onSubmit={(identity) => identifyGuest(identity)}
config={{
title: "Welcome to Support",
subtitle: "Please introduce yourself to get started",
submitButtonText: "Start Chat",
}}
/>
);
}
return (
<div>
{messages.map((msg, i) => (
<div key={i}>{msg.role}: {msg.content}</div>
))}
<input
onKeyDown={(e) => {
if (e.key === "Enter") sendMessage(e.currentTarget.value);
}}
disabled={isLoading}
/>
</div>
);
}
// app/support/page.tsx — Server Component (no "use client")
import { createCustomerServerConfig } from "@syncagent/nextjs/server";
import { GuestChat } from "./guest-chat";
export default async function SupportPage() {
const config = createCustomerServerConfig({
apiKey: process.env.SYNCAGENT_KEY!,
connectionString: process.env.DATABASE_URL!,
externalUserId: undefined, // omit to enable guest flow
});
return <GuestChat config={config} />;
}
When externalUserId is not provided, the useCustomerChat hook requires guest identification before messages can be sent. The GuestIdentificationForm collects the guest's details and the identity is persisted to localStorage so returning visitors are recognized automatically.
⚠️ Deprecated:
createDual()will be removed in a future major version. UsecreateDualServerConfig()instead — it keeps your connection string on the server and returns separatedbandsupportconfigs for Client Components.
Before (deprecated):
import { SyncAgentClient } from "@syncagent/js";
const { db, support } = SyncAgentClient.createDual({
apiKey: process.env.SYNCAGENT_KEY!,
connectionString: process.env.DATABASE_URL!,
externalUserId: session.user.id,
});
After (recommended):
import { createDualServerConfig } from "@syncagent/nextjs/server";
const { db, support } = createDualServerConfig({
apiKey: process.env.SYNCAGENT_KEY!,
connectionString: process.env.DATABASE_URL!,
externalUserId: session.user.id,
});
Legacy pattern (deprecated):
// app/dashboard/page.tsx — Server Component
import { SyncAgentClient } from "@syncagent/js";
import { getServerSession } from "next-auth";
import { AdminChat } from "./admin-chat";
import { CustomerWidget } from "./customer-widget";
export default async function DashboardPage() {
const session = await getServerSession();
const { db, support } = SyncAgentClient.createDual({
apiKey: process.env.SYNCAGENT_KEY!,
connectionString: process.env.DATABASE_URL!,
externalUserId: session.user.id,
});
// Pass each client to its respective widget
return (
<div>
<AdminChat config={db} />
<CustomerWidget config={support} />
</div>
);
}
The createDualServerConfig() server helper creates a unified dual-mode configuration from a Server Component. It returns separate db and support config objects that you pass to Client Components — keeping your connection string out of the browser bundle while enabling both database agent and customer agent modes.
// app/dashboard/page.tsx — Server Component
import { createDualServerConfig } from "@syncagent/nextjs/server";
import { getServerSession } from "next-auth";
import { AdminChat } from "./admin-chat";
import { CustomerWidget } from "./customer-widget";
export default async function DashboardPage() {
const session = await getServerSession();
const { db, support } = createDualServerConfig({
apiKey: process.env.SYNCAGENT_KEY!,
connectionString: process.env.DATABASE_URL!,
externalUserId: session.user.id,
filter: { organizationId: session.user.orgId },
});
return (
<div>
<AdminChat config={db} />
<CustomerWidget config={support} />
</div>
);
}
DualServerConfig Return Typeinterface DualServerConfig {
db: SyncAgentConfig;
support: SyncAgentConfig;
}
db — Configuration for the database agent mode (uses chat())support — Configuration for the customer agent mode (uses customerChat())createDualServerConfig() validates the provided options and throws an error if required fields are missing:
| Condition | Error Message |
|---|---|
apiKey is missing | @syncagent/nextjs: apiKey is required |
externalUserId is missing | @syncagent/nextjs: externalUserId is required for dual mode |
connectionString is missing (and toolsOnly is not true) | @syncagent/nextjs: connectionString is required (or set toolsOnly: true) |
When
toolsOnly: trueis set,connectionStringis not required — the agent will only use custom tools without database access.
useDualChatuseDualChat is re-exported from @syncagent/nextjs for client-side use — no need to install @syncagent/react separately:
import { useDualChat } from "@syncagent/nextjs";
Use useDualChat in your Client Components to manage both database and customer chat state from a single hook. See the @syncagent/react documentation for full useDualChat() API reference.
Functions like onBeforeToolCall can't be serialized from Server Components. Define them in a Client Component:
// app/components/chat.tsx — "use client"
import { SyncAgentChat } from "@syncagent/nextjs";
export function Chat({ serverConfig }) {
return (
<SyncAgentChat
config={{
...serverConfig,
onBeforeToolCall: (name, args) => {
console.log(`[Audit] ${name}`, args);
return true;
},
}}
/>
);
}
All components and hooks from @syncagent/react are re-exported:
import { SyncAgentChat, SyncAgentProvider, useSyncAgent, SyncAgentClient } from "@syncagent/nextjs";
| Function | Returns | Context | Description |
|---|---|---|---|
createServerConfig(options) | SyncAgentConfig | Server only | Create config with all options |
createServerConfigFromEnv(overrides?) | SyncAgentConfig | Server only | Create config from SYNCAGENT_KEY + DATABASE_URL env vars |
createCustomerServerConfig(options) | SyncAgentConfig | Server only | Create config for customer agent mode (requires externalUserId) |
createDualServerConfig(options) | DualServerConfig | Server only | Create unified dual-mode config requiring externalUserId — returns { db, support } |
The following types and hooks are re-exported from @syncagent/nextjs for client-side use:
import { useDualChat, DualChatReturn, UseDualChatOptions } from "@syncagent/nextjs";
| Export | Kind | Description |
|---|---|---|
useDualChat | Hook | Manages both database and customer chat state from a single hook |
DualChatReturn | Type | Return type of useDualChat() — contains db and support namespaces |
UseDualChatOptions | Type | Options interface for useDualChat() — accepts context, onData, onEscalated, onResolved |
These are re-exported from @syncagent/react so you don't need to install it separately.
createServerConfig ensures the connection string stays in the server bundleNEXT_PUBLIC_ prefix for your database URL| Plan | Requests/mo | Collections | Price |
|---|---|---|---|
| Free (+ 14-day trial) | 100 (500 during trial) | 5 | GH₵0 |
| Starter | 5,000 | 20 | GH₵150/mo |
| Pro | 50,000 | Unlimited | GH₵500/mo |
| Enterprise | Unlimited | Unlimited | Custom |
MIT
FAQs
SyncAgent Next.js SDK — server-safe helpers and components
The npm package @syncagent/nextjs receives a total of 139 weekly downloads. As such, @syncagent/nextjs popularity was classified as not popular.
We found that @syncagent/nextjs demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Research
/Security News
Compromised npm package art-template delivered a Coruna-like iOS Safari exploit framework through a watering-hole attack.

Company News
As AI accelerates how code is written and shipped, Socket is scaling to protect the software supply chain from the growing wave of attacks targeting open source dependencies.

Company News
Socket is scaling to defend open source against supply chain attacks as AI accelerates software development.