skills/react-core/references/provider-setup.md
Mount the CopilotKit provider (from @copilotkit/react-core/v2) 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.
Which provider component? Always use
CopilotKitimported from@copilotkit/react-core/v2. It is the compatibility bridge across v1 and v2 and a strict superset of the other provider APIs. Do not useCopilotKitfrom the package root (@copilotkit/react-core, legacy v1) orCopilotKitProviderfrom/v2(a subset of the functionality).
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 { CopilotKit } from "@copilotkit/react-core/v2";
import "@copilotkit/react-core/v2/styles.css";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<CopilotKit
runtimeUrl="/api/copilotkit"
credentials="include"
onError={({ code, error, context }) => {
console.error("[copilotkit]", code, error, context);
}}
>
{children}
</CopilotKit>
);
}
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 { CopilotKit } from "@copilotkit/react-core/v2";
import "@copilotkit/react-core/v2/styles.css";
export function App({ children }: { children: React.ReactNode }) {
return <CopilotKit runtimeUrl="/api/copilotkit">{children}</CopilotKit>;
}
<CopilotKit publicLicenseKey="ck_pub_..." />
publicLicenseKey is the canonical prop for running CopilotKit from a
pure client bundle. publicApiKey is a deprecated alias that resolves to
the same value — accept it in old code, but always write
publicLicenseKey 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.
<CopilotKit
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],
);
<CopilotKit runtimeUrl="/api/copilotkit" properties={properties} />;
Wrong:
// app/page.tsx (server component — no "use client")
import { CopilotKit } from "@copilotkit/react-core/v2";
export default function Page() {
return <CopilotKit runtimeUrl="/api/copilotkit">...</CopilotKit>;
}
Correct:
// app/providers.tsx
"use client";
import { CopilotKit } from "@copilotkit/react-core/v2";
export function Providers({ children }: { children: React.ReactNode }) {
return <CopilotKit runtimeUrl="/api/copilotkit">{children}</CopilotKit>;
}
// 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:
<CopilotKit
agents__unsafe_dev_only={{
default: new BuiltInAgent({ apiKey: process.env.OPENAI_KEY! }),
}}
/>
// or the alias (same thing):
<CopilotKit
selfManagedAgents={{ default: new BuiltInAgent({ apiKey: "..." }) }}
/>
Correct:
// Route through a runtime that keeps secrets server-side:
<CopilotKit runtimeUrl="/api/copilotkit" />
// Or for a pure SPA, use CopilotKit Intelligence:
<CopilotKit publicLicenseKey="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:
<CopilotKit
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],
);
<CopilotKit
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:
<CopilotKit runtimeUrl="/api/copilotkit" />
Correct:
<CopilotKit
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
publicApiKey in new codeWrong:
<CopilotKit publicApiKey="ck_pub_..." />
Correct:
<CopilotKit publicLicenseKey="ck_pub_..." />
publicApiKey still works as a deprecated alias, but publicLicenseKey
is the canonical name. The CopilotKit provider resolves
publicLicenseKey || publicApiKey. Always write the canonical form in
new code.
Source: packages/react-core/src/components/copilot-provider/copilotkit.tsx:172
Wrong:
<html>
<body>
<Header></Header>
<CopilotKit>{children}</CopilotKit>
</body>
</html>
Correct:
<html>
<body>
<CopilotKit>
<Header />
{children}
</CopilotKit>
</body>
</html>
Any component that calls useCopilotKit, useFrontendTool, useAgent, or
any other CopilotKit hook must be a descendant of the CopilotKit
provider. Placing the provider beside or below a consumer throws at mount.
Source: packages/react-core/src/v2/providers/CopilotKitProvider.tsx (context)