Why Track User Behavior?

Understanding how users interact with your website is crucial for improving the user experience. Behavior analytics tools help identify friction points, optimize conversions, and provide insights into how users navigate your application.

Why Microsoft Clarity?

Microsoft Clarity is a free behavior analytics tool that offers:
✅ Heatmaps – Visualize where users click and scroll.
✅ Session Recordings – Replay user sessions to understand their journey.
✅ Insights – Identify pain points and engagement metrics.
✅ Google Analytics Integration – Combine behavioral data with standard analytics.

In this guide, we’ll integrate Microsoft Clarity into a Next.js app and ensure it runs efficiently without affecting server-side rendering (SSR). Let’s get started!


Video of Clarity dashboard


1. Create a next app

If you don’t have an existing Next.js project, create one using:

npx create-next-app ms-clarity-example

2. Install Microsoft Clarity

Install the Clarity package using:

npm install @microsoft/clarity

3. Get Your Clarity Project ID

2.1. Create a project via
https://clarity.microsoft.com/

2.2. Navigate to Settings → Overview and copy the Project ID.


4. Initializing Clarity in Next.js

To start tracking user sessions, we need to initialize Clarity using:

Clarity.init();

🚨 The Catch: Run Clarity on the Client Side Only

  • Clarity should not run on the server side, as it relies on window.
  • We must use useEffect to ensure it runs only in the browser.
  • The best place to initialize Clarity is in the top-level layout.tsx file.

❌ Initial Approach: Adding it Directly in RootLayout.tsx

"use client"

/* Rest of the code */
export default function RootLayout({
    children,
}: {
    children: React.ReactNode;
}) {

    useEffect(() => {
        if (typeof window !== "undefined") {
            Clarity.init(process.env.NEXT_PUBLIC_CLARITY_ID!);
        }
      }, []);

/* Rest of the code */

⚠️ Problem: RootLayout Becomes a Client Component

By adding "use client", we force RootLayout to be client-rendered, preventing Next.js from utilizing SSR and static generation. Instead, we should move Clarity initialization to a separate component.


Hook vs a component

Hook

If the logic is moved to a hook we have the same problem RootLayout needs to be a client component.

import Clarity from "@microsoft/clarity";
import { useEffect } from "react";

export default function useMsClarity() {
    useEffect(() => {
        if (typeof window !== "undefined") {
            Clarity.init(process.env.NEXT_PUBLIC_CLARITY_ID!);
        }
      }, []);
}

// Rootlayout.tsx
"use client"
export default function RootLayout({
    children,
}: {
    children: React.ReactNode;
}) {

    // use hook
      useMsClarity();

Component

Lets create a client component to hold the logic

"use client";
import Clarity from "@microsoft/clarity";
import { useEffect } from "react";

export default function useMsClarity() {
    useEffect(() => {
        if (typeof window !== "undefined") {
            Clarity.init();
        }
      }, []);

    return null;
}

Use the component in root layout.

import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import BaseLayout from "./ui/layouts/BaseLayout";
import ClarityInit from "./ui/analytics/clarity-init";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
    title: "MS clarity test",
    description: "MS clarity test",
};

export default function RootLayout({
    children,
}: {
    children: React.ReactNode;
}) {

    return (
        
            
                
                
                    {children}
                
            
        
    );
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    




By moving the logic to a separate component (ClarityInit), we avoid making RootLayout a client component, allowing the rest of the app to benefit from Server-Side Rendering (SSR).
  
  
  6. Store the Clarity Project ID Securely
finally move the clarity-project-id to an env variable to make sure that it doe not pushed to version control system.

Clarity.init(process.env.NEXT_PUBLIC_CLARITY_ID!);



    Enter fullscreen mode
    


    Exit fullscreen mode
    




Note 
The NEXT_PUBLIC_ prefix is needed so that nextjs can read the env variable.add it to .env file as well

NEXT_PUBLIC_CLARITY_ID=




    Enter fullscreen mode
    


    Exit fullscreen mode
    




Ms-clarity will start to collect sessions and you can see them in the dashboard.git hub link with code example.
https://github.com/chamupathi/ms-clarity