How to Integrate Plausible with Your Headless CMS
Connect Plausible to your headless CMS so teams can track pageviews, conversions, and content performance by slug, author, category, campaign, and publish status.
What is Plausible?
Plausible is a privacy-focused web analytics platform that tracks visitors, pageviews, referrers, campaigns, goals, custom events, and conversions without cookies by default. Itās commonly used by marketing, product, and engineering teams that want simple traffic reporting, GDPR-friendly analytics, and a lighter alternative to Google Analytics. Plausible supports a JavaScript tracking script, custom event tracking, an Events API, and a Stats API for pulling reporting data into other tools.
Why integrate Plausible with a headless CMS?
Analytics gets more useful when page data and content data meet. Plausible can tell you that /blog/launch-notes had 18,420 unique visitors, a 41% bounce rate, and 312 newsletter signups. Your content back end knows the author, topic, persona, publish date, CTA variant, localization status, and related product. When you connect the two, you can answer better questions, like which author drives the most trial signups, which category holds attention the longest, or which campaign pages convert after translation.,A headless CMS category setup often gives you an API, but the quality of the integration depends on the shape of the content and how fast changes are exposed. With Sanityās AI Content Operating System, content is structured as typed JSON in the Content Lake, so you can query exactly the fields Plausible needs with GROQ. Webhooks can fire on publish, update, or delete events, and Functions can run server-side code immediately without a separate queue, cron job, or small service you have to maintain.,The disconnected alternative is familiar. Someone exports Plausible CSV reports, matches URLs to a spreadsheet of authors and campaigns, fixes slug mismatches by hand, and repeats the work next week. It works for 20 pages. It breaks when you have 2,000 pages, localized routes, canonical URLs, split tests, or multiple front ends using the same content.
Architecture overview
A typical Sanity and Plausible integration has two flows. First, the front end loads Plausibleās script or npm tracker on every public page and sends pageviews and custom events to Plausible with the page URL, domain, and optional custom properties. Second, Sanity sends content-change events to Plausible or to your app when content is published, updated, or deleted. In Sanity, a webhook can trigger only for published documents, for example blog posts where defined(slug.current). That webhook can call a Sanity Function, a Next.js API route, or another small HTTPS handler. The handler receives the document ID, uses @sanity/client to fetch the latest fields from the Content Lake with GROQ, builds the canonical URL, and calls Plausibleās Events API at https://plausible.io/api/event with a custom event such as Content Published. For reporting, your app or Sanity Studio tool can also call Plausibleās Stats API with a Plausible API key to pull metrics for a slug, then display visits, sources, goals, and conversions next to the content entry. The trade-off is important: Plausible is an analytics system, not a content index. Use Sanity as the source of structured content and Plausible as the source of traffic and conversion data.
Common use cases
Content performance by taxonomy
Join Plausible page metrics with Sanity fields like category, persona, author, and publish date to see which content groups produce signups or demo requests.
Goal tracking for editorial CTAs
Send Plausible custom events for newsletter clicks, trial buttons, pricing links, or gated asset downloads tied to the Sanity document that rendered the CTA.
Publish event tracking
Fire a Plausible custom event when a post or landing page is published so teams can compare traffic before and after launch.
Localized page reporting
Map localized Sanity routes to Plausible URLs so you can compare performance across /en, /fr, /de, and other language paths.
Step-by-step integration
- 1
Set up Plausible for your site
Create a Plausible account, add your domain, and install the tracking script in your front end, for example <script defer data-domain="example.com" src="https://plausible.io/js/script.js"></script>. If you need app-side reporting, create a Plausible API key under account settings for the Stats API. If you prefer npm-based tracking, install plausible-tracker and send custom events from your app.
- 2
Model analytics fields in Sanity Studio
Add fields that make analytics joins predictable: slug, canonicalUrl or analyticsPath, author reference, category reference, campaignGroup, locale, publishDate, and noIndex. Keep the analytics path explicit if your routing rules are complex, because Plausible reports by URL.
- 3
Create a publish webhook or Function
In Sanity, create a webhook that triggers on publish events for the relevant document types, such as post, landingPage, or docsPage. Use a GROQ filter like _type in ["post", "landingPage"] && defined(slug.current). Point the webhook to a Sanity Function or an HTTPS route in your app.
- 4
Fetch the content shape Plausible needs
Inside the handler, use @sanity/client and GROQ to fetch only the fields required for analytics: title, slug, locale, author name, category title, and canonical URL. GROQ can follow references in one query, so you donāt need extra requests for author or taxonomy data.
- 5
Call Plausibleās API
For publish or operational events, POST to Plausibleās Events API at /api/event with the domain, URL, event name, and props. For dashboards inside Sanity Studio or your internal app, call Plausibleās Stats API with your API key to read metrics for a specific URL.
- 6
Test with a real URL
Test on a preview or staging domain registered in Plausible, not localhost. Publish one test document, confirm the webhook fired, check that the event appears in Plausibleās real-time view, and verify that the page URL matches the route your front end renders.
Code example
import {createClient} from '@sanity/client'
import type {NextRequest} from 'next/server'
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: '2025-01-01',
token: process.env.SANITY_READ_TOKEN,
useCdn: false,
})
const query = `*[_id == $id][0]{
title,
"slug": slug.current,
"author": author->name,
"category": category->title
}`
export async function POST(req: NextRequest) {
const body = await req.json()
const id = body._id || body.ids?.updated?.[0] || body.ids?.created?.[0]
if (!id) return Response.json({skipped: 'Missing document ID'}, {status: 400})
const doc = await sanity.fetch(query, {id})
if (!doc?.slug) return Response.json({skipped: 'Missing slug'})
const domain = process.env.PLAUSIBLE_DOMAIN!
const url = `https://${domain}/blog/${doc.slug}`
const plausible = await fetch('https://plausible.io/api/event', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': 'Sanity webhook',
'X-Forwarded-For': '127.0.0.1',
},
body: JSON.stringify({
name: 'Content Published',
domain,
url,
props: {
title: doc.title,
author: doc.author || 'Unknown',
category: doc.category || 'Uncategorized',
},
}),
})
if (!plausible.ok) throw new Error(await plausible.text())
return Response.json({sent: true, url})
}How Sanity + Plausible works
Build your Plausible integration on Sanity
Sanity gives you the structured content foundation, real-time event system, GROQ queries, and flexible APIs to connect Plausible analytics to the content your teams ship.
Start building free āCMS approaches to Plausible
| Capability | Traditional CMS | Sanity |
|---|---|---|
| URL-to-content matching | Often depends on rendered pages, plugins, or manual exports, so analytics usually stays at the URL level. | Uses GROQ to fetch the route plus referenced author, category, locale, and campaign fields in one query. |
| Publish-triggered analytics events | Usually handled by a plugin or a custom server hook tied to that platformās page lifecycle. | Webhooks and Functions can react to publish, update, and delete events and call Plausibleās Events API from server-side code. |
| Custom event context | CTA clicks and conversion events may not include structured fields like author, persona, or content type. | Typed content fields can be rendered into the page or fetched server-side, then passed to Plausible custom event props. |
| Editorial analytics views | Editors often switch between the publishing interface, Plausible, and spreadsheets to understand performance. | Sanity Studio is React-based, so teams can build a panel that reads Plausibleās Stats API for the current document URL. |
| Multi-channel reporting | Analytics is usually centered on website pages, with mobile, docs, and campaign surfaces tracked separately. | One schema can define shared fields like campaignGroup, contentType, and locale for web, mobile, Plausible events, and AI agents. |
| Implementation trade-off | Fast if a plugin covers your exact setup, harder when routes, locales, or custom goals get specific. | You still write the mapping logic, but GROQ, webhooks, and Functions reduce the amount of external infrastructure. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Google Analytics
Connect structured content fields with GA4 events and conversions for deeper campaign and funnel reporting.
Sanity + Segment
Send content-aware events from your site into Segment, then route them to analytics, marketing, and warehouse tools.
Sanity + Amplitude
Tie product behavior to Sanity-powered pages, docs, onboarding flows, and in-app content experiments.