Mastering Analytics with PostHog in AstroJS

Published on

Embark on a data-driven journey with your AstroJS website by seamlessly integrating PostHog. This guide ensures you capture every interaction, server-side operation, and client-side event with precision. Ready to turn interactions into actionable insights?

Setting Up PostHog

Begin integrating PostHog to unlock comprehensive analytics capabilities.


Installation

Server (For server-side tracking):

Terminal window
npm install posthog-node

Client (For client-side tracking):

Terminal window
npm install posthog-js

Initialization

Proper initialization ensures accurate data capture.

Server

In your server-side TypeScript code:

import PostHog from 'posthog-node';
const posthog = new PostHog('YOUR_API_KEY', {
apiHost: 'YOUR_POSTHOG_INSTANCE'
});

Client-Side in AstroJS

Initialize PostHog in your Astro components using client:load:

Create a PostHog Astro component with the client:load directive to configure PostHog on any of your pages. Example:

~/src/components/PostHog.astro
---
---
<!-- HTML and component logic -->
<script type="module" client:load>
import { posthog } from "../../node_modules/posthog-js";
posthog.init('YOUR_API_KEY', {
api_host: 'YOUR_POSTHOG_INSTANCE',
});
</script>

You can now add this component to any page or layout that you’d like to have PostHog configured on. Here’s an example using an Astro layout.

---
import PostHog from "@/components/analytics/PostHog.astro";
---
<!doctype html>
<html lang="en">
<head>
</head>
<body>
<PostHog />
<slot name="main" />
</body>
</html>

Key Concept: distinctId


Throughout the rest of this article we’re going to be referring to a value called a distinctId.

The distinctId in the PostHog API is a unique identifier that represents an individual user or entity within your application or website. It allows you to track and analyze user behavior and interactions, enabling you to understand how users engage with your product. This identifier is crucial for identifying and attributing actions and events to specific users or entities, helping you gain insights into user behavior and improve your product’s user experience.

Basic Usage: Capturing Events

Here we’ll show the most basic implementation of posthog event capture, both a server and a client side example. In these examples there is no link between the user’s client events and server events, as the distinctId on the client is not shared with the one used on the server.


Server-Side Events

Capture server-side events with a distinctId:

posthog.capture({
distinctId: 'unique-user-id',
event: 'server-event-name',
properties: {
key: 'value'
}
});

Client-Side Events

Capture client-side events, in the most basic implementation posthog client side library will create and manage the distinctId on its own.

posthog.capture('client-event-name', {
property1: 'value1',
property2: 'value2'
});

Advanced Usage: Linking Client and Server Events Using Cookies in AstroJS

Maintain a consistent distinctId across client and server using cookies, simplifying the linking of events.


  1. Generate a distinctId server-side and pass it to the client using a distinct_id cookie, the distinctId allows the client and server to share an id, used for tracking user analytics.

    • On the server, generate a distinctId and store it in a cookie.
      • If the user is authenticated, using the authenticated user’s id is a great option
      • If the user is not logged in we can generate any uuid and use that as the distinctId
    • For this example we’re going to be using AstroJS middleware, to create and set the distinct_id cookie.
    ~/src/middleware/distinct-id.ts
    import type { APIContext, MiddlewareNext } from "astro";
    import { defineMiddleware } from "astro/middleware";
    import { v4 as uuidv4 } from 'uuid';
    export const distinctIdMiddleware = defineMiddleware((context: APIContext, next: MiddlewareNext) => {
    // If there is an existint distinctId lets use it.
    let distinctId = context.cookies.get('distinct_id')?.value;
    // If this is the first request we'll generate a new distinctId
    if (!distinctId) {
    distinctId = uuidv4();
    }
    // Store the distinctId in the Astro.locals for use throught the request lifecycle
    // as well as having a shared distinctId betwen the server and the client
    context.locals.distinctId = distinctId;
    // Set the distinctId on the cookie for use on the client.
    context.cookies.set('distinct_id', distinctId, {
    maxAge: 60 * 60 * 24 * 365,
    path: '/'
    });
    next();
    });
  2. Register the distinctIdMiddleware with AstroJS, this will allow for access to the distinctId for the rest of the request, including page renders and api handlers.

    ~/src/middleware/index.ts
    // sequence will accept middleware functions and will execute them in the order they are passed
    import { sequence } from "astro/middleware";
    // Import the middleware
    import { distinctIdMiddleware } from "./distinct-id";
    // export onRequest. Invoke "sequence" with the middleware
    export const onRequest = sequence(distinctIdMiddleware);
  3. Utilize Astro.locals to access the distinctId and capture events with PostHog in your API handlers.

    • This is useful if your application is going to be logging server events as well as client side events.
    • Example:
    src/pages/api/your-api-endpoint.ts
    import PostHog from 'posthog-node';
    export async function post({ Astro }) {
    const posthog = new PostHog('YOUR_API_KEY', {
    apiHost: 'YOUR_POSTHOG_INSTANCE'
    });
    // Access the distinctId added by our distinctIdMiddleware
    const distinctId = Astro.locals.distinctId;
    // Capture and event and send it to posthog
    posthog.capture({
    distinctId,
    event: 'api-event-name',
    });
    return new Response('Event tracked', { status: 200 });
    }
  4. Read the shared distinctId Cookie from a PostHog AstroJS component Client side:

    • Update the PostHog.astro component to initialize the client side posthog instance, with the shared distinctId previously set in the cookie.
    ~/src/components/PostHog.astro
    ---
    ---
    <!-- HTML and component logic -->
    <script type="module" client:load>
    import { posthog } from "../../node_modules/posthog-js";
    const cookies = document.cookie.split('; ').reduce((prev, current) => {
    const [name, value] = current.split('=');
    prev[name] = value;
    return prev;
    }, {});
    const distinctId = cookies['distinct_id'];
    // Initialize the posthog client instance with the `distinctId` from the server
    posthog.init('YOUR_API_KEY', {
    api_host: 'YOUR_POSTHOG_INSTANCE',
    bootstrap: {
    distinctID: distinctId,
    },
    });
    </script>
  5. Linking the distinctId to the authenticated user.

    • Now that your app is able to share a distinctId between the client and the server, you can go one step further and actually associate this id with an authenticated user’s metadata, we’ll use their email as an example.
    • This is a very simple process and can happen immediately after a user has authenticated.
    ~/pages/api/signin.ts
    import PostHog from 'posthog-node';
    export const POST: APIRoute = async ({ url, request, cookies, redirect, locals }) => {
    // Sign in pseudocode
    const formData = await request.formData();
    const email = formData.get("email");
    const password =formData.get("password");
    const signInResult = await signIn(email, password)
    if (signInResult.success) {
    // Here is where we'll identify aka associate our generated `distinctId`
    // with the authenticated user.
    const posthog = new PostHog('YOUR_API_KEY', {
    apiHost: 'YOUR_POSTHOG_INSTANCE'
    });
    posthog.identify({
    distinctId: locals.distinctId,
    properties: {
    email: email,
    },
    });
    return new Response('Sign in success', { status: 200 });
    } else {
    throw new Error("Unable to sign in")
    }
    };

Conclusion

Replace 'YOUR_API_KEY' and 'YOUR_POSTHOG_INSTANCE' with your actual PostHog details. Ensure proper initialization before capturing any events. Customize your tracking strategy to the nature of your events and AstroJS’s capabilities.

Your AstroJS website is now a beacon of analytics, crafting a unified narrative of user interactions and system performance. Steer through your PostHog dashboard with the clarity of data-driven insights, propelling your decisions to unparalleled success. Here’s to a future where every interaction is a gateway to profound insights! 🚀