Capture, compress, and stream client sessions natively to your ANALAS server. Add the tracker directly to your website or import it in your SPA.
Add this snippet in the <head> of your website to record sessions automatically:
<!-- Load the ANALAS Session Recorder -->
<script src="/session-recorder.js"></script>
<script>
AnalasRecorder.init({
apiKey: "your-workspace-api-key",
sampleRate: 0.2, // Record only 20% of sessions (0-1)
maskAllText: true, // Enable strict full-text masking
excludePaths: ["/billing", "/settings"] // Skip tracking these routes
});
</script>For React, Next.js, or Single Page Apps, import and initialize the tracker inside your root layout or app wrapper:
import { initSessionRecorder } from "@/lib/session-recorder";
import { useEffect } from "react";
export default function RootProvider({ children }) {
useEffect(() => {
// Starts the recorder and returns a cleanup function
const cleanup = initSessionRecorder({
apiKey: "your-workspace-api-key",
sampleRate: 0.5, // Record 50% of sessions
excludePaths: ["/admin"] // Exclude paths
});
return cleanup;
}, []);
return children;
}Insight types are defined in src/lib/insight-types.ts as plain objects. Adding a new type takes three steps.
Add an entry to the INSIGHT_TYPES array. Each entry must satisfy the InsightTypeDef interface:
interface InsightTypeDef {
id: string; // unique key, e.g. "funnel"
label: string; // shown in the type selector
description: string; // one-sentence explanation
icon: string; // emoji or single character
configFields: {
key: string; // stored in queryConfig JSON
label: string;
placeholder: string;
}[];
docs?: Record<string, {
description: string; // Simple, non-technical explanation
useCases: string[]; // Direct examples of when to use it
fields: Record<string, string>; // Description of each configField key
}>;
}Example:
{
id: "funnel",
label: "Funnel",
description: "Conversion rate between two events.",
icon: "⬇",
configFields: [
{ key: "fromEvent", label: "From event", placeholder: "page_view" },
{ key: "toEvent", label: "To event", placeholder: "user_signup" },
],
docs: {
en: {
description: "Measures progression through sequence steps.",
useCases: ["Tracking user checkout signup flow"],
fields: {
fromEvent: "The initial step event name",
toEvent: "The target event name to reach"
}
},
fa: {
description: "سنجش میزان پیشرفت و تبدیل در مراحل مختلف.",
useCases: ["ردیابی مراحل پرداخت و خرید کاربر"],
fields: {
fromEvent: "نام رویداد برای مرحله اول",
toEvent: "نام رویداد هدف برای رسیدن به آن"
}
}
}
}configFields will automatically generate input boxes in the UI, and their values will be saved to the database without any extra code.Open src/app/api/workspace/[workspaceId]/insights/[insightId]/data/route.ts and add a branch for your type id. The route receives the insight's queryConfig (the values from your configFields) and the workspace's tenantId. Return:
// Required shape
{ total: number; rows: { day: string; count: number }[] }
// rows can be empty for non-time-series typesOpen src/app/workspace/[workspaceId]/insights/insight-card.tsx and add a render branch inside the card body:
{insight.type === "funnel" && (
<FunnelRenderer data={data} />
)}The card handles loading skeletons and error states for you — just focus on the visual.
eventNameeventNametimeFramedisplayTypeeventNamepropertyeventNamestimeFramedisplayTypeeventStepsdistinctIdeventNameaggregationpropertytimeFramedisplayTypestartEventstartEventPropertystartEventValuereturnEventreturnEventPropertyreturnEventValuedistinctId