Privacy-first post analytics for EmDash CMS. Know how your content performs without compromising your readers' privacy.
No cookies. No fingerprinting. No third-party services. All data stays in your EmDash instance.
- Views per post with unique visitor approximation
- Read rate based on real scroll depth and attention time
- Scroll depth milestones (25%, 50%, 75%, 100%)
- Engaged views (10s+ active attention with interaction)
- Recirculation tracking (clicks to internal links)
- Time on page via active attention tracking (visible + focused, 60s inactivity timeout)
- Referrer breakdown showing where your traffic comes from
- Trend indicators comparing current period vs previous
- Admin dashboard widget with top posts at a glance
- Full analytics page with timeseries charts, scroll depth funnel, top posts table, and referrer pie chart
If you need a broader analytics layer for campaigns, funnels, goals, forms, and custom events, pair this plugin with em-analytics-hub.
em-content-insightsfocuses on post performance, reading behavior, and editorial analytics.em-analytics-hubfocuses on site-wide analytics, campaign insights, conversions, and configurable funnels/goals.- They can coexist in the same EmDash installation when you want both content analytics and business analytics.
npm install em-content-insights// astro.config.mjs
import { postAnalytics } from "em-content-insights";
export default defineConfig({
integrations: [
emdash({
plugins: [postAnalytics()],
}),
],
});---
// src/layouts/Base.astro (or your base layout)
import BeaconScript from "em-content-insights/astro";
---
<html>
<body>
<slot />
<BeaconScript />
</body>
</html>That's it. The <BeaconScript /> component inlines a lightweight tracking script (~1.5KB) that automatically detects post content and starts tracking.
The plugin works out of the box with zero configuration. Optionally customize the path prefix:
postAnalytics({
pathPrefix: "/blog/", // default: "/posts/"
})Or change it at runtime from the admin UI under Settings > Post Analytics > Post URL Prefix.
- The
<BeaconScript />component renders an inline script on every page - On page load, it sends a
viewevent vianavigator.sendBeacon() - As the reader scrolls, it sends
scrollevents at 25%, 50%, 75%, and 100% depth milestones - After 10 seconds of active attention with interaction, it sends an
engagedevent - When the reader has scrolled 75%+ and spent enough time reading (estimated from content length), it sends a
readevent - If the reader clicks an internal link, it sends a
recircevent - When the reader leaves, it sends a
pingwith total active attention time
All events hit a public plugin route (POST /_emdash/api/plugins/post-analytics/track) that validates the payload, filters bots, and stores the data.
The plugin uses a hybrid storage model:
- Raw events (
eventscollection) preserve full granularity for future analysis - Daily aggregates (
daily_statscollection) power fast admin queries
The admin UI reads from daily aggregates, so dashboard performance is constant regardless of traffic volume.
| Concern | How it's handled |
|---|---|
| Cookies | None |
| IP addresses | Hashed with a daily-rotating salt. Raw IPs are never stored. |
| Fingerprinting | None |
| Cross-page tracking | None. Each pageview is independent. |
| Do Not Track | Honored. If navigator.doNotTrack === "1", the beacon does nothing. |
| Bot filtering | User-Agent based filtering for known crawlers |
Shows on the EmDash admin home page:
- Total views and visitors for the last 7 days with trend indicators
- Top 5 posts ranked by views
Accessible from the admin sidebar under Post Insights:
- Date range selector (7 / 30 days)
- Stat cards: total views, unique visitors, read rate, avg time on page
- Timeseries chart with views, engaged views, and reads over time
- Scroll depth funnel chart
- Top posts table sortable by views, visitors, read rate, and avg time
- Referrer pie chart
- Country breakdown (when running on Cloudflare)
All routes are available at /_emdash/api/plugins/post-analytics/.
| Route | Auth | Method | Description |
|---|---|---|---|
track |
Public | POST | Receives beacon events |
stats |
Admin | GET | Aggregated stats. Params: pathname, days |
top-posts |
Admin | GET | Ranked posts. Params: days, limit |
- EmDash
^0.1.0 - Astro
^6.0.0 - No external services or API keys required
git clone https://github.com/facuzarate04/em-content-insights.git
cd em-content-insights
npm install
npm testnpm test # single run
npm run test:watch # watch modesrc/
index.ts # Plugin descriptor (build time)
sandbox-entry.ts # Plugin definition: hooks, routes, admin UI (runtime)
beacon.ts # Client-side tracking script generator
astro/
BeaconScript.astro # Drop-in Astro component for the tracking beacon
__tests__/ # Vitest test suite
| Capability | Purpose |
|---|---|
read:content |
Resolve post titles for admin display |
network:fetch |
Reserved for future license validation |
page:inject |
Auto-inject beacon via page:fragments (when supported by EmDash runtime) |