/ astro-integrations / How to Use PostHog with Astro: Complete Guide
astro-integrations 5 min read

How to Use PostHog with Astro: Complete Guide

Step-by-step guide to integrating PostHog with your Astro website.

PostHog is an all-in-one product analytics platform that goes far beyond pageview tracking. It includes session replay, feature flags, A/B testing, surveys, and a data warehouse, all in one tool. The free tier gives you 1 million events per month, which is generous enough that most startups and side projects never need to pay. For Astro developers building products (not just content sites), PostHog provides the kind of deep user insights that help you make better product decisions.

The integration involves adding PostHog's JavaScript snippet to your Astro site. From there, it automatically captures pageviews, clicks, and session data. You then add custom events for specific user actions you want to track.

Prerequisites

  • Node.js 18+
  • An Astro project (npm create astro@latest)
  • A PostHog account (free for 1 million events/mo, then usage-based pricing)
  • Your PostHog project API key

Installation

Install the PostHog JavaScript client:

npm install posthog-js

Configuration

Get your API key and instance address from PostHog's project settings.

Add them to .env:

PUBLIC_POSTHOG_KEY=phc_your_api_key_here
PUBLIC_POSTHOG_HOST=https://us.i.posthog.com

If you are self-hosting PostHog, replace the host with your instance URL.

Create a PostHog initialization component:

---
// src/components/PostHogAnalytics.astro
const posthogKey = import.meta.env.PUBLIC_POSTHOG_KEY;
const posthogHost = import.meta.env.PUBLIC_POSTHOG_HOST;
---

{posthogKey && (
  <script define:vars={{ posthogKey, posthogHost }}>
    !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing startSessionRecording stopSessionRecording sessionRecordingStarted loadToolbar get_property getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSurvey getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
    posthog.init(posthogKey, {
      api_host: posthogHost,
      person_profiles: 'identified_only',
      capture_pageview: true,
      capture_pageleave: true,
    });
  </script>
)}

Add this to your base layout:

---
// src/layouts/BaseLayout.astro
import PostHogAnalytics from "../components/PostHogAnalytics.astro";
---

<html>
  <head>
    <PostHogAnalytics />
  </head>
  <body>
    <slot />
  </body>
</html>

Basic Usage

With PostHog initialized, it automatically captures pageviews and basic interactions. For custom event tracking, use the PostHog client in your components:

---
// src/components/PricingCard.astro
const { plan, price } = Astro.props;
---

<div class="pricing-card">
  <h3>{plan}</h3>
  <p class="price">${price}/mo</p>
  <button class="subscribe-btn" data-plan={plan} data-price={price}>
    Subscribe
  </button>
</div>

<script>
  document.querySelectorAll(".subscribe-btn").forEach((btn) => {
    btn.addEventListener("click", (e) => {
      const target = e.currentTarget as HTMLElement;
      window.posthog?.capture("pricing_click", {
        plan: target.dataset.plan,
        price: target.dataset.price,
      });
    });
  });
</script>

Using feature flags to conditionally show content:

---
// src/pages/index.astro
---

<div id="hero-section">
  <h1 id="hero-title">Default Hero</h1>
  <p id="hero-subtitle"></p>
</div>

<script>
  window.posthog?.onFeatureFlags(() => {
    const isNewHero = window.posthog?.isFeatureEnabled("new-hero-design");
    if (isNewHero) {
      const title = document.getElementById("hero-title");
      const subtitle = document.getElementById("hero-subtitle");
      if (title) title.textContent = "New Hero Design";
      if (subtitle) subtitle.textContent = "This is the A/B test variant";
    }
  });
</script>

For View Transitions support, re-capture pageviews on navigation:

---
// src/components/PostHogViewTransitions.astro
---

<script>
  document.addEventListener("astro:after-swap", () => {
    window.posthog?.capture("$pageview");
  });
</script>

Identify users after authentication:

// After login
window.posthog?.identify(userId, {
  email: user.email,
  name: user.name,
  plan: user.subscription,
});

// After logout
window.posthog?.reset();

Production Tips

  1. Use person_profiles: 'identified_only' to save on events. By default, PostHog creates anonymous person profiles for every visitor. Setting this to identified_only means person profiles are only created when you call identify(), which dramatically reduces your event count on the free tier.

  2. Enable session replay selectively. Session replay is powerful for debugging but uses significant event quota. Enable it only for specific pages or user segments using PostHog's configuration options rather than globally across your entire site.

  3. Use feature flags for gradual rollouts. Instead of deploying features and hoping for the best, use PostHog feature flags to roll out to 10% of users first. Monitor the impact in analytics, then increase to 100% when confident.

  4. Set up key funnels. Define conversion funnels in the PostHog dashboard (e.g., landing page to signup to first action to subscription). This tells you where users drop off and what to fix first.

  5. Self-host for data control. PostHog offers a self-hosted option via Docker. If you need full data ownership or have strict compliance requirements, self-hosting gives you all the features without sending data to a third party.

Alternatives to Consider

  • Fathom or Plausible if you only need pageview analytics and want a simpler, privacy-focused tool without the product analytics features.
  • Mixpanel if you want a more established product analytics platform with deeper analysis tools and a focus on event-based tracking.
  • Google Analytics if you need free analytics with advertising integration, though at the cost of more complexity and privacy concerns.

Wrapping Up

PostHog is the analytics tool for Astro developers building products, not just content sites. The combination of event tracking, session replay, feature flags, and A/B testing in one platform means fewer tools to manage and better data to act on. The free tier at 1 million events per month covers most early-stage projects comfortably, and the self-hosted option means you can scale without worrying about costs or data residency. If you are making product decisions and need more than pageview counts, PostHog gives you the depth to understand what your users actually do.