Frameworks
Set up @knock-ai/sdk in React, Next.js, Vue, Angular, plain JavaScript or with a CDN script.
Each framework binding calls init() for you. The React, Next.js and Vue snippets read your product tag ID from an environment variable and only set up Knock when it's set, because init() doesn't check the ID. The SDK supports React 17+, Vue 3+ and Angular 17+, in the browser (not React Native).
React (Vite)
import { KnockProvider, useKnock } from '@knock-ai/sdk/react';
import App from './App';
const knockTagId = import.meta.env.VITE_KNOCK_PRODUCT_TAG_ID;
export function Root({ user }: { user: { email: string; firstName?: string } | null }) {
if (!knockTagId) return <App />;
return (
<KnockProvider
tagId={knockTagId}
user={user ? { traits: { email: user.email, firstName: user.firstName } } : null}
>
<App />
</KnockProvider>
);
}
// Anywhere inside the provider
export function ExportButton() {
const knock = useKnock();
return <button onClick={() => knock.track('report_exported', { format: 'csv' })}>Export</button>;
}KnockProvidercallsinit()once, on mount, andidentify()wheneveruseris set or changes. ChangingtagIdlater does nothing, and settingusertonulldoesn't sign anyone out.<KnockButton>Book a meeting</KnockButton>opens your default modal. PassmagicLinkIdto open a specific one, which also starts loading it on hover or focus.<KnockLink href="https://start-chat.com/slack/acme/sales">Chat with sales</KnockLink>renders a plain<a>withhrefas is. It adds the visitor's identity only while the link is opened (click, middle-click, Enter or a mouse press), so a copied link stays clean.- Hooks:
useKnock(),useKnockIdentify(traits),useKnockEvent(event, handler).
Next.js (App Router)
@knock-ai/sdk/react is marked 'use client', so KnockProvider can go straight into your root layout, which stays a server component.
import { KnockProvider } from '@knock-ai/sdk/react';
const knockTagId = process.env.NEXT_PUBLIC_KNOCK_PRODUCT_TAG_ID;
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{knockTagId ? <KnockProvider tagId={knockTagId}>{children}</KnockProvider> : children}</body>
</html>
);
}To identify a user you read on the server, render a small client component inside KnockProvider, for example <KnockIdentify email={user.email} />:
'use client';
import { useKnockIdentify } from '@knock-ai/sdk/react';
export function KnockIdentify(traits: { email: string; firstName?: string }) {
useKnockIdentify(traits);
return null;
}- Call the hooks only from client components.
KnockButtonandKnockLinkalso render from server components, as long as you pass them no event handlers. - Next.js writes
NEXT_PUBLIC_variables into the browser code at build time, so set the variable where the build runs.
Vue
import { createApp } from 'vue';
import { KnockPlugin } from '@knock-ai/sdk/vue';
import App from './App.vue';
const knockTagId = import.meta.env.VITE_KNOCK_PRODUCT_TAG_ID;
const app = createApp(App);
if (knockTagId) app.use(KnockPlugin, { tagId: knockTagId });
app.mount('#app');<script setup lang="ts">
import { onMounted } from 'vue';
import { useKnock, KnockButton } from '@knock-ai/sdk/vue';
const props = defineProps<{ user: { email: string; firstName?: string } }>();
const knock = useKnock();
onMounted(() => knock.identify({ email: props.user.email, firstName: props.user.firstName }));
</script>
<template>
<button @click="knock.track('report_exported', { format: 'csv' })">Export</button>
<KnockButton>Book a meeting</KnockButton>
</template>KnockPlugincallsinit()once, when the app installs it.KnockButtonworks like the React one. In templates, write the prop asmagic-link-id.- Vue has no
KnockLink. On your own link, callknock.wrapLink(url)from@clickand@auxclick. Skip right-clicks, and put the plain URL back right after. See Keep identity on chat links.
Angular
import { ApplicationConfig } from '@angular/core';
import { provideKnock } from '@knock-ai/sdk/angular';
import { environment } from '../environments/environment';
export const appConfig: ApplicationConfig = {
providers: [provideKnock({ tagId: environment.knockProductTagId })],
};Angular has no public environment variables: keep the ID in your environment.ts, or paste it in directly. It isn't a secret.
import { Component, Input, OnInit, inject } from '@angular/core';
import { KNOCK } from '@knock-ai/sdk/angular';
@Component({
selector: 'app-toolbar',
standalone: true,
template: `<button (click)="knock.modal.open()">Book a meeting</button>`,
})
export class ToolbarComponent implements OnInit {
@Input({ required: true }) user!: { email: string; firstName?: string };
readonly knock = inject(KNOCK);
ngOnInit() {
this.knock.identify({ email: this.user.email, firstName: this.user.firstName });
}
}provideKnock()callsinit()once, when the app starts. In anNgModuleapp, add it to the root module'sproviders.inject(KNOCK)returns the sameknockobject as@knock-ai/sdk.- Angular has no button or link component. Call
knock.modal.open()from your own button. On your own link, callknock.wrapLink(url)from(click)and(auxclick). Skip right-clicks, and put the plain URL back right after. See Keep identity on chat links.
Plain JavaScript
This example is a marketing site, so it uses your website tag ID.
import { knock } from '@knock-ai/sdk';
knock.init({ tagId: 'YOUR_WEBSITE_TAG_ID' });
document.querySelector('#demo-form')?.addEventListener('submit', (event) => {
const email = new FormData(event.target).get('email');
if (email) knock.identify({ email }); // website tag: captures a lead
knock.track('demo_form_submitted');
});
document.querySelector('#book-demo')?.addEventListener('click', () => knock.modal.open());CDN
No build step: load the SDK from jsDelivr.
<!-- once, in <head> -->
<script src="https://cdn.jsdelivr.net/npm/@knock-ai/sdk@0.1/dist/snippet.js"></script>
<script>knockai.init({ tagId: 'YOUR_WEBSITE_TAG_ID' });</script>- Load the snippet without
asyncordefer, soknockaiexists when the next script runs. - Any script after it can call
knock.identify(),knock.track()orknock.modal.open()right away. If your page already uses the nameknock, useknockaiinstead (same object). - With a Content Security Policy, the inline
<script>needs a nonce, a hash or'unsafe-inline'. See Content Security Policy.