skills/react-core/references/provider-setup.md
Mount CopilotKitProvider once near the root of the React tree. Every
CopilotKit hook (useAgent, useFrontendTool, useRenderTool, etc.) and
every chat component (CopilotChat, CopilotPopup, CopilotSidebar) must
be rendered inside this provider.
All v2 imports use the @copilotkit/react-core/v2 subpath. Imports from the
package root are v1 and will not work with v2 hooks or components.
@copilotkit/react-core/v2 is marked "use client". You must mount the
provider from a client component, not a server component. The cleanest
pattern is a dedicated client-only providers.tsx.
// app/providers.tsx
"use client";
import { CopilotKitProvider } from "@copilotkit/react-core/v2";
import "@copilotkit/react-core/v2/styles.css";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<CopilotKitProvider
runtimeUrl="/api/copilotkit"
credentials="include"
onError={({ code, error, context }) => {
console.error("[copilotkit]", code, error, context);
}}
>
{children}
</CopilotKitProvider>
);
}
For auth headers that change over the session (rotating bearer tokens,
refreshed cookies), see the "Stable headers for rotating auth tokens"
pattern below. Avoid putting a useMemo(() => ({ Authorization: ... }), []) on the provider — an empty deps array captures the token at mount
and never refreshes.
// app/layout.tsx — server component
import { Providers } from "./providers";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
import { CopilotKitProvider } from "@copilotkit/react-core/v2";
import "@copilotkit/react-core/v2/styles.css";
export function App({ children }: { children: React.ReactNode }) {
return (
<CopilotKitProvider runtimeUrl="/api/copilotkit">
{children}
</CopilotKitProvider>
);
}
<CopilotKitProvider publicApiKey="ck_pub_..." />
publicApiKey is the canonical prop for running CopilotKit from a pure
client bundle. publicLicenseKey is an alias that resolves to the same
value (publicApiKey ?? publicLicenseKey) — accept in old code, but
always write publicApiKey in new code.
For tokens that change during the session, use the imperative setter instead
of re-rendering the provider with a new headers prop.
"use client";
import { useCopilotKit } from "@copilotkit/react-core/v2";
import { useEffect } from "react";
export function AuthTokenSync({ token }: { token: string }) {
const { copilotkit } = useCopilotKit();
useEffect(() => {
copilotkit.setHeaders({ Authorization: `Bearer ${token}` });
}, [copilotkit, token]);
return null;
}
onError fires for every CopilotKitCoreErrorCode emitted by core. Keeps
UI from getting stuck in "connecting..." when the runtime URL is wrong or
CORS is misconfigured.
<CopilotKitProvider
runtimeUrl="/api/copilotkit"
onError={({ code, error, context }) => {
telemetry.capture({ code, message: error.message, context });
}}
/>
properties flows to the runtime on each agent run — useful for tenant IDs,
feature flags, or anything the server needs.
const properties = useMemo(
() => ({ tenantId: user.tenantId, locale: user.locale }),
[user.tenantId, user.locale],
);
<CopilotKitProvider runtimeUrl="/api/copilotkit" properties={properties} />;
Wrong:
// app/page.tsx (server component — no "use client")
import { CopilotKitProvider } from "@copilotkit/react-core/v2";
export default function Page() {
return (
<CopilotKitProvider runtimeUrl="/api/copilotkit">...</CopilotKitProvider>
);
}
Correct:
// app/providers.tsx
"use client";
import { CopilotKitProvider } from "@copilotkit/react-core/v2";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<CopilotKitProvider runtimeUrl="/api/copilotkit">
{children}
</CopilotKitProvider>
);
}
// app/layout.tsx imports <Providers>.
@copilotkit/react-core/v2 begins with "use client". Importing it from a
server component silently strips interactivity — the provider renders but
none of the hooks wire up.
Source: packages/react-core/src/v2/index.ts:1
agents__unsafe_dev_only or selfManagedAgents in productionWrong:
<CopilotKitProvider
agents__unsafe_dev_only={{
default: new BuiltInAgent({ apiKey: process.env.OPENAI_KEY! }),
}}
/>
// or the alias (same thing):
<CopilotKitProvider
selfManagedAgents={{ default: new BuiltInAgent({ apiKey: "..." }) }}
/>
Correct:
// Route through a runtime that keeps secrets server-side:
<CopilotKitProvider runtimeUrl="/api/copilotkit" />
// Or for a pure SPA, use CopilotKit Cloud:
<CopilotKitProvider publicApiKey="ck_pub_..." />
Both props are aliases for the same dev-only mechanism and ship any embedded credentials to the browser bundle. Never use either for production agents.
Source: packages/react-core/src/v2/providers/CopilotKitProvider.tsx:136-138,393
Wrong:
<CopilotKitProvider
runtimeUrl="/api/copilotkit"
headers={{ Authorization: `Bearer ${token}` }}
properties={{ tenantId: user.tenantId }}
/>
Correct:
const headers = useMemo(() => ({ Authorization: `Bearer ${token}` }), [token]);
const properties = useMemo(
() => ({ tenantId: user.tenantId }),
[user.tenantId],
);
<CopilotKitProvider
runtimeUrl="/api/copilotkit"
headers={headers}
properties={properties}
/>;
New object identity on every render causes the provider to diff-churn
internal state and may thrash tool/renderer registration. useStableArrayProp
also logs a console.error when array-prop shape changes without
memoization.
Source: packages/react-core/src/v2/providers/CopilotKitProvider.tsx:324-340,399-410
onError leaves users stuck in "connecting..."Wrong:
<CopilotKitProvider runtimeUrl="/api/copilotkit" />
Correct:
<CopilotKitProvider
runtimeUrl="/api/copilotkit"
onError={({ code, error, context }) => {
telemetry.capture({ code, error, context });
}}
/>
Without onError, connection failures (bad runtime URL, CORS, network) keep
the provider in a provisional state with ProxiedCopilotRuntimeAgent
instances that never resolve. The chat UI keeps showing "connecting..."
forever and users never see the actual error.
Source: packages/react-core/src/v2/providers/CopilotKitProvider.tsx:638-660
publicLicenseKey in new codeWrong:
<CopilotKitProvider publicLicenseKey="ck_pub_..." />
Correct:
<CopilotKitProvider publicApiKey="ck_pub_..." />
publicLicenseKey still works as an alias, but publicApiKey is the
canonical name in v2. The provider resolves
publicApiKey ?? publicLicenseKey. Always write the canonical form in
new code.
Source: packages/react-core/src/v2/providers/CopilotKitProvider.tsx:122-128,391
Wrong:
<html>
<body>
<Header></Header>
<CopilotKitProvider>{children}</CopilotKitProvider>
</body>
</html>
Correct:
<html>
<body>
<CopilotKitProvider>
<Header />
{children}
</CopilotKitProvider>
</body>
</html>
Any component that calls useCopilotKit, useFrontendTool, useAgent, or
any other CopilotKit hook must be a descendant of CopilotKitProvider.
Placing the provider beside or below a consumer throws at mount.
Source: packages/react-core/src/v2/providers/CopilotKitProvider.tsx (context)