Tracking Web and Product Analytics with PostHog

Understanding your users is crucial to the success of your web application.

Whether you're a startup or an established company, having access to some data around usage and engagement helps you make better decisions.

However, any discussion around web analytics needs a caveat: not everything can be described by a data point on a chart. Sometimes you need to talk to users. Other times, your gut and intuition are your best friends!

With that said, PostHog has an excellent offering that helps you understand usage as well as test variations (using A/B testing) and perform feature rollouts (with feature flags). Let's take a look.

By the way, I've made a video based on this blog post! You can check it out below.

Importance of Analytics in Growth

With user analytics, you can understand which features are most popular and which are essentially unused.

I've developed features before that nobody ended up using. But were users trying out the feature, and deciding they didn't need it? Or was the feature too hidden, and users didn't even know it was there?

Good analytics (and talking with your users) helps you detect which of these happened.

By analyzing product usage, sign-ups, and onboarding processes, you can identify patterns that guide your next big feature or enhancement.

Web analytics provide further granularity by revealing page views, session durations, and user flows across your marketing and content pages. Understanding these metrics is vital for optimizing user experience and conversion rate.

Feature Flags and A/B Testing

PostHog's feature flags offer the flexibility of progressively rolling out new features of your application.

For example, you could make a new feature available to just 5% of your users with a feature flag. If you see any glaring errors, you can turn the feature off without affecting your entire user base.

A/B testing is an extension of this concept. By presenting different versions of a page to different user segments, you can determine which performs better, which can help maximize conversions.

Session Replay and User Insight

Session replay is another powerful feature of PostHog. It provides a visual playback of user interactions, allowing you to diagnose errors and refine the user journey with precision.

Getting Started with PostHog

Firstly, you'll need to create a PostHog account. Register here and familiarize yourself with the dashboard. To track analytics, you can integrate PostHog into your application in several ways.

For Web Clients

Embedding PostHog into your web application begins with setting up the web client. You will include a script in your base HTML template that initializes PostHog and starts tracking page events.

Here’s a quick setup guide:

Insert the PostHog JavaScript snippet into the <head> section of your base.html template:

<!– base.html -->
<head>
    <script>
        <!-- PostHog initialization code -->
        <!-- PostHog user identification code -->
    </script>
</head>

Replace <!-- PostHog initialization code --> with the script provided by PostHog.

Remember to configure the script with your 'Project API Key' to begin tracking page loads and views.

Then, you'll want to identify users. Each user in your application should be identifiable with a unique ID. Assigning each user a unique ID is done in the Python code. More on that in a moment!

For Custom Events with Python API

To identify users and to send custom product analytics events, you'll use PostHog's Python library:

Install the PostHog library using pip:

pip install posthog

In your Python application, import and configure PostHog with your API key:

import posthog

posthog.api_key = 'your-posthog-api-key'
posthog.host = 'https://app.posthog.com'  # or eu.posthog.com

Capture events by calling posthog.capture, for example:

posthog.capture('user-id', 'User Signed Up')

Remember to replace 'user-id' with the actual identifier of your user.

But how do you get a user identifier? You use the database user ID, but there's a bit more to that... Read on!

Identifying registered and anonymous users with PostHog and Flask

Most (all?) users start their visit to a website as anonymous users. They don't have an account, and they're not logged in.

Then at some point they may register or log in.

In subsequent visits they may start the journey as logged-in users, or not. If their session has expired, they'll need to log in again.

This is to say, that our tracking needs to be able to track anonymous and registered users, and it also needs to handle when an anonymous user becomes a registered user, and make sure any captured data is stored under the registered user data.

For anonymous users, we'll define a unique ID and store that in the browser cookies. Every time they make a page request, that same user ID will be re-used. For registered users, we'll use their database ID.

from flask import session
from flask_security_too import current_user
import uuid
import posthog

posthog.api_key = 'your-posthog-api-key'
posthog.host = 'https://app.posthog.com'

@app.before_request
def before_request():
    if "user_id" not in session:
        if current_user.is_authenticated:
            session["user_id"] = current_user.id
        else:
            session["user_id"] = str(uuid.uuid4())

    # When an anonymous user signs in, PostHog aliases the new authenticated user ID with the UUID.
    if current_user.is_authenticated and current_user.id != session.get("user_id"):
        posthog.alias(session["user_id"], current_user.id)
        session["user_id"] = current_user.id

In the code above, we use the before_request Flask hook to check if a session-level user ID exists. If not, we assign one.

For authenticated users, we use their unique current_user.id from Flask-Security-Too. If you are using a different library, just replace that by your user's ID that is stored in the database.

For anonymous users, we generate and assign a UUID (str(uuid.uuid4())).

When an anonymous user registers or logs in, we use posthog.alias to merge the UUID session with the authenticated ID.

In your template, you'll then want to run this:

<!-- base.html -->
<head>
    <!-- Other head elements -->

    <!-- Start PostHog -->
    <script>
        <!-- PostHog initialization code -->

        <!-- PostHog user identification code -->
        posthog.identify("{{ session['user_id'] }}")
    </script>
    <!-- End PostHog -->
</head>

With this, you have consistent tracking, so you know what your users do before and after they authenticate!

Identifying registered and anonymous users with PostHog and Django

Doing this with Django is similar to with Flask, but instead of using the before_request hook, you'd write a middleware:

import uuid
import posthog

posthog.api_key = 'your-posthog-api-key'
posthog.host = 'https://app.posthog.com'  # or eu.posthog.com

def posthog_middleware(get_response):
	def middleware(request):
		if "user_id" not in request.session:
	        if request.user.is_authenticated:
	            request.session["user_id"] = request.user.id
	        else:
	            request.session["user_id"] = str(uuid.uuid4())

	    # When an anonymous user signs in, PostHog aliases the new authenticated user ID with the UUID.
	    if request.user.is_authenticated and request.user.id != session.get("user_id"):
	        posthog.alias(session["user_id"], request.user.id)
	        session["user_id"] = request.user.id

Then you can add this middleware to MIDDLEWARE in your settings.py file, making sure it is after other middlewares that must run first, such as for the session and authentication:

MIDDLEWARE = [
    # ... (other middleware entries)
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',

    # Add posthog_middleware after AuthenticationMiddleware
    'path.to.your.application.posthog_middleware',

    # ... (other middleware entries)
]

For more information on middleware, I strongly recommend reading the official documentation.

Then in your base template, you'd again call posthog.identify(), passing your user ID.

Conclusion

I really like PostHog, and it's super easy to get started with it! I use it for product analytics, web analytics, observing session replays, implementing feature flags, and A/B testing.

If you want to learn more about web development with Python and Flask, including web app design, HTML, and CSS, check out our Web Developer Bootcamp. It's a complete course that teaches you how to build web applications!

In our next PostHog article we'll look at working with feature flags using PostHog. I'll link it below when it's published next week!