Skip to content
KnockDevelopers

Frameworks

Set up @knock-ai/sdk in React, Next.js, Vue, Angular, plain JavaScript or with a CDN script.

Chat with a Developer

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)

src/Root.tsx
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>;
}
  • KnockProvider calls init() once, on mount, and identify() whenever user is set or changes. Changing tagId later does nothing, and setting user to null doesn't sign anyone out.
  • <KnockButton>Book a meeting</KnockButton> opens your default modal. Pass magicLinkId to 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> with href as 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.

app/layout.tsx
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} />:

app/knock-identify.tsx
'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. KnockButton and KnockLink also 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

src/main.ts
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');
src/components/Toolbar.vue
<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>
  • KnockPlugin calls init() once, when the app installs it.
  • KnockButton works like the React one. In templates, write the prop as magic-link-id.
  • Vue has no KnockLink. On your own link, call knock.wrapLink(url) from @click and @auxclick. Skip right-clicks, and put the plain URL back right after. See Keep identity on chat links.

Angular

src/app/app.config.ts
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.

src/app/toolbar.component.ts
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() calls init() once, when the app starts. In an NgModule app, add it to the root module's providers.
  • inject(KNOCK) returns the same knock object as @knock-ai/sdk.
  • Angular has no button or link component. Call knock.modal.open() from your own button. On your own link, call knock.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.

main.js
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.

index.html
<!-- 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 async or defer, so knockai exists when the next script runs.
  • Any script after it can call knock.identify(), knock.track() or knock.modal.open() right away. If your page already uses the name knock, use knockai instead (same object).
  • With a Content Security Policy, the inline <script> needs a nonce, a hash or 'unsafe-inline'. See Content Security Policy.
Chat with a Developer

On this page