Code·September 5, 2025

Setting up PostHog for Nuxt

How I set up Posthog for Nuxt frontend + Nitro backend

PostHog Dashboard

Attribution: posthog.com

The PostHog docs are great for setting up for Nuxt. This is how I set it up and includes server-side (Nitro) error tracking (not included in the PostHog setup).

PostHog for frontend

1. Install posthog-js :

npm install --save posthog-js

2. Add your public PostHog key to your runtimeConfig in nuxt.config.ts

nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      posthogPublicKey: '<public_posthog_key>',
    }
  }
})

3. Create posthog.client.ts in ~/app/plugins (or ~/plugins in Nuxt 3)

app/plugins/posthog.client.ts
import posthog from "posthog-js";

export default defineNuxtPlugin((nuxtApp) => {
  const runtimeConfig = useRuntimeConfig();

  // Skip PostHog initialization in development
  if (import.meta.dev) {
    console.log("PostHog disabled in development mode");
    return;
  }

  const posthogClient = posthog.init(runtimeConfig.public.posthogPublicKey, {
    api_host: "/ingest",
    ui_host: "https://us.posthog.com",
    person_profiles: "identified_only", // or 'always' to create profiles for anonymous users as well
  });

  nuxtApp.hook("vue:error", (error) => {
    posthogClient.captureException(error);
  });

  return {
    provide: {
      posthog: () => posthogClient,
    },
  };
});

PostHog for backend (Nitro)

1. Install posthog-node

npm install --save posthog-node

2. Create posthog.ts in ~/server/plugins/posthog.ts

server/plugins/posthog.ts
import { PostHog } from "posthog-node";

// HTTP methods that typically include request bodies
const METHODS_WITH_BODY = new Set(["POST", "PUT", "PATCH"]);

const hasRequestBody = (method: string) => METHODS_WITH_BODY.has(method);

export default defineNitroPlugin((nitro) => {
  // Skip PostHog initialization in development
  if (import.meta.dev) {
    console.log("PostHog disabled in development mode (server)");
    return;
  }

  // Capture server-side errors
  nitro.hooks.hook("error", async (error, { event }) => {
    if (!event) return;

    const config = useRuntimeConfig(event);
    const client = new PostHog(config.public.posthogPublicKey);

    client.captureException(error, undefined, {
      path: event.path,
      method: event.method,
      query: JSON.stringify(getQuery(event)),
      headers: JSON.stringify(getHeaders(event)),
      body: hasRequestBody(event.method) ? readRawBody(event) : undefined,
    });

    await client.shutdown();
  });
});

Conclusion

This setup will give you PostHog analytics in the frontend + PostHog error tracking in the backend. Happy coding!

Hey! 👋

I hope you enjoyed this post!

I post whatever is on my mind, whether it's life, code, theology, or something random. Follow me on X if you'd like a heads up on new posts.