Janus Client SDK
A lightweight JavaScript library for tracking events from web applications. Automatically handles batching, retries, rate limiting, and persistence.
Lightweight (~3KB)Auto RetryBatchingPersistence
Framework Guides
Next.js — App Router#
The App Router uses React Server Components by default. Load the Janus script with next/script and track route changes with the usePathname hook. Disable trackSPANavigation to avoid duplicate pageviews from the App Router's internal replaceState calls.
app/layout.tsx
import Script from 'next/script';export default function RootLayout({children,}: {children: React.ReactNode;}) {return (<html lang="en"><body>{children}<Scriptsrc="https://addjanus.ca/janus.js"strategy="afterInteractive"/><Script id="janus-init" strategy="afterInteractive">{`Janus.init('jns_your_api_key', {trackSPANavigation: false});`}</Script></body></html>);}
app/components/Analytics.tsx
'use client';import { usePathname } from 'next/navigation';import { useEffect } from 'react';// declare global { interface Window { Janus: any } }export function Analytics() {const pathname = usePathname();useEffect(() => {// Track page view on route changewindow.Janus?.page();}, [pathname]);return null;}// Add <Analytics /> to your layout.tsx inside <body>
Next.js — Pages Router#
The Pages Router works well with the default SPA tracking. Add the script in _document.tsx and initialize in _app.tsx.
pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';export default function Document() {return (<Html><Head><scriptsrc="https://addjanus.ca/janus.js"defer/></Head><body><Main /><NextScript /></body></Html>);}
pages/_app.tsx
import type { AppProps } from 'next/app';import { useEffect } from 'react';// declare global { interface Window { Janus: any } }export default function App({ Component, pageProps }: AppProps) {useEffect(() => {window.Janus?.init('jns_your_api_key');}, []);return <Component {...pageProps} />;}
Nuxt 3#
Create a client-only plugin that loads the script dynamically and tracks route changes with the Vue Router afterEach hook.
plugins/janus.client.ts
// declare global { interface Window { Janus: any } }export default defineNuxtPlugin(() => {const router = useRouter();// Load the script dynamicallyconst script = document.createElement('script');script.src = 'https://addjanus.ca/janus.js';script.onload = () => {window.Janus.init('jns_your_api_key', {trackSPANavigation: false});};document.head.appendChild(script);// Track route changes via Vue Routerrouter.afterEach(() => {window.Janus?.page();});});
Alternative: nuxt.config.ts#
If you prefer a simpler setup without a plugin, add the script via the Nuxt config head. Note: this uses the default SPA tracking, which works well with Nuxt's client-side navigation.
nuxt.config.ts
export default defineNuxtConfig({app: {head: {script: [{ src: 'https://addjanus.ca/janus.js' },{children: "Janus.init('jns_your_api_key');",},],},},});
Remix#
Add the script tag in root.tsx and use useLocation to track route changes. Disable trackSPANavigation for clean control over page tracking.
app/root.tsx
import { useEffect } from 'react';import { useLocation, Scripts, Links, Outlet } from '@remix-run/react';// declare global { interface Window { Janus: any } }export default function App() {const location = useLocation();useEffect(() => {window.Janus?.init('jns_your_api_key', {trackSPANavigation: false});}, []);// Track route changesuseEffect(() => {window.Janus?.page();}, [location.pathname]);return (<html lang="en"><head><Links /></head><body><Outlet /><script src="https://addjanus.ca/janus.js" /><Scripts /></body></html>);}
Astro#
Add an inline script in your Layout component. Astro renders static HTML by default, so each page load triggers a fresh init.
src/layouts/Layout.astro
---// Layout.astro---<html lang="en"><head><script is:inline src="https://addjanus.ca/janus.js"></script><script is:inline>Janus.init('jns_your_api_key');</script></head><body><slot /></body></html>
View Transitions#
If you use Astro's View Transitions, pages swap without a full reload. Listen for the astro:page-load event to track each navigation:
src/layouts/Layout.astro
<script is:inline src="https://addjanus.ca/janus.js"></script><script is:inline>// Initialize once on first loadJanus.init('jns_your_api_key', {trackSPANavigation: false});// Track page views on View Transitions navigationdocument.addEventListener('astro:page-load', () => {Janus.page();});</script>
SvelteKit#
Load the script dynamically in your root layout using onMount, and use afterNavigate to track route changes.
src/routes/+layout.svelte
<script>import { onMount } from 'svelte';import { afterNavigate } from '$app/navigation';// Typing: declare global { interface Window { Janus: any } }onMount(() => {const script = document.createElement('script');script.src = 'https://addjanus.ca/janus.js';script.onload = () => {window.Janus.init('jns_your_api_key', {trackSPANavigation: false});};document.head.appendChild(script);});afterNavigate(() => {window.Janus?.page();});</script><slot />
Need Help?
If you have questions or run into issues, check out our troubleshooting guide or reach out to our support team.
Troubleshooting Guide