This document provides a comprehensive overview of Plausible Analytics implementation for pyplots.ai, including all tracked events, user interactions, and the required Plausible dashboard configuration.
Location: app/index.html
- Self-hosted Plausible script loaded via
/js/script.js - Custom endpoint:
/api/event(proxied through nginx to avoid adblockers) - Manual pageview tracking:
autoCapturePageviews: false - Production-only: Only tracks on
pyplots.aidomain - Privacy-focused: No cookies, GDPR-compliant
Implementation: Query parameters converted to URL path segments for better analytics segmentation.
Filters create dynamic URLs with the following format:
https://pyplots.ai/{category}/{value}/{category}/{value}/...
Ordered categories: lib, spec, plot, data, dom, feat, dep, tech, pat, prep, style
Examples:
/?lib=matplotlib→https://pyplots.ai/lib/matplotlib/?lib=matplotlib&plot=scatter→https://pyplots.ai/lib/matplotlib/plot/scatter/?lib=matplotlib,seaborn→https://pyplots.ai/lib/matplotlib,seaborn(OR logic)/?lib=matplotlib&lib=seaborn→https://pyplots.ai/lib/matplotlib/lib/seaborn(AND logic)
Benefits:
- Plausible shows popular filter combinations
- No manual event tracking needed for filter changes
- URL structure clearly shows user's browsing context
| URL | Description |
|---|---|
/ |
Home page (no filters) |
/catalog |
Catalog page (alphabetical spec list) |
/legal |
Legal notice, privacy policy, transparency |
/mcp |
MCP server documentation (AI assistant integration) |
/stats |
Platform statistics (library scores, coverage, tags, top implementations) |
/{spec_id} |
Spec overview page (grid of all implementations) |
/{spec_id}/{library} |
Spec detail page (single library implementation) |
/interactive/{spec_id}/{library} |
Interactive fullscreen view (HTML plots) |
Total pageview tracking: Automated via trackPageview() in all pages
| Event Name | Properties | Where | Description |
|---|---|---|---|
copy_code |
spec, library, method, page |
ImageCard.tsx, SpecPage.tsx, SpecTabs.tsx | User copies code to clipboard |
download_image |
spec, library, page |
SpecPage.tsx | User downloads PNG image |
Copy methods:
card: Quick copy button on image card (home grid)image: Copy button on main image (spec page)tab: Copy button in Code tab
Page values (for user journey tracking):
home: HomePage grid viewspec_overview: SpecPage showing all library implementationsspec_detail: SpecPage showing single library implementation
| Event Name | Properties | Where | Description |
|---|---|---|---|
search |
query, category |
FilterBar.tsx | User searches and selects value |
search_no_results |
query |
FilterBar.tsx | Search query returns no results (debounced 200ms) |
| Event Name | Properties | Where | Description |
|---|---|---|---|
random_filter |
category, value, method |
useFilterState.ts | User triggers random filter |
filter_remove |
category, value |
useFilterState.ts | User removes a filter |
grid_resize |
size |
ToolbarActions.tsx | User toggles between normal/compact view |
tab_toggle |
action, tab, library |
SpecTabs.tsx | User opens or closes a tab |
catalog_rotate |
spec |
CatalogPage.tsx | User clicks image in catalog to rotate library |
open_interactive |
spec, library |
SpecOverview.tsx, SpecDetailView.tsx | User opens interactive HTML view |
suggest_spec |
- | CatalogPage.tsx | User clicks "suggest spec" link |
report_issue |
spec, library? |
SpecPage.tsx | User clicks "report issue" link |
tag_click |
param, value, source |
SpecTabs.tsx | User clicks a tag chip to filter |
Random methods:
click: Shuffle icon clickedspace: Spacebar presseddoubletap: Mobile double-tap gesture
Grid sizes:
normal: Larger cards (1-3 columns)compact: Smaller cards (2-6 columns)
Tab toggle actions:
open: User opened a tabclose: User closed a tab
Tab names: code, specification, implementation, quality
| Event Name | Properties | Where | Description |
|---|---|---|---|
external_link |
destination, spec?, library? |
Footer.tsx, LegalPage.tsx | User clicks external link |
internal_link |
destination, spec?, library? |
Footer.tsx | User clicks internal link in footer |
External destinations (Footer): github, stats, linkedin
External destinations (LegalPage): linkedin, x, github_personal
Internal destinations: mcp, legal
| Event Name | Properties | Where | Description |
|---|---|---|---|
LCP |
value, rating |
reportWebVitals.ts | Largest Contentful Paint (rounded to nearest 100ms) |
CLS |
value, rating |
reportWebVitals.ts | Cumulative Layout Shift (2 decimal places) |
INP |
value, rating |
reportWebVitals.ts | Interaction to Next Paint (rounded to nearest 50ms) |
Rating values: good, needs-improvement, poor (per web-vitals thresholds)
CWV tracking is production-only and dynamically imported (zero dev/bundle cost).
Social media bots (Twitter, WhatsApp, Teams, etc.) don't execute JavaScript, so og:image requests can only be tracked server-side.
All og:images are routed through the API for tracking:
Bot requests page → nginx detects bot → SEO proxy serves HTML with og:image URL
↓
https://api.pyplots.ai/og/{endpoint}.png
↓
track_og_image() → Plausible Events API
↓
Return image (fire-and-forget tracking)
Implementation: api/analytics.py (server-side Plausible tracking)
| Event Name | Properties | Description |
|---|---|---|
og_image_view |
page, platform, spec?, library?, filter_*? |
Bot requested og:image |
| Property | Values | Description |
|---|---|---|
page |
home, catalog, spec_overview, spec_detail |
Page type |
platform |
See list below | Detected platform from User-Agent |
spec |
Specification ID | Only for spec pages |
library |
Library ID | Only for detail pages |
filter_* |
Filter value | Dynamic props for filtered URLs (e.g., filter_lib, filter_dom) |
Social Media: twitter, facebook, linkedin, pinterest, reddit, tumblr, mastodon
Messaging Apps: slack, discord, telegram, whatsapp, signal, viber, skype, teams, snapchat
Search Engines: google, bing, yandex, duckduckgo, baidu, apple
Link Preview Services: embedly, quora, outbrain, rogerbot, showyoubot
Fallback: unknown
Some apps (Signal, others) use a WhatsApp User-Agent to bypass rate limits (Issue #10060). We distinguish real WhatsApp from spoofed requests by version format:
| Platform | User-Agent Example | Detection |
|---|---|---|
whatsapp |
WhatsApp/2.23.18.78 i |
3+ part version = verified WhatsApp |
whatsapp-lite |
WhatsApp or WhatsApp/2 |
Simplified UA = Signal or other spoofers |
| Endpoint | Description | Tracking |
|---|---|---|
/og/home.png |
Static og:image for home page | page=home, filter_* from query params |
/og/catalog.png |
Static og:image for catalog | page=catalog |
/og/{spec_id}.png |
Collage og:image for spec overview | page=spec_overview, spec |
/og/{spec_id}/{library}.png |
Branded og:image for implementation | page=spec_detail, spec, library |
When users share filtered URLs (e.g., https://pyplots.ai/?lib=plotly&dom=statistics), the filters are passed to the og:image endpoint:
og:image URL: https://api.pyplots.ai/og/home.png?lib=plotly,matplotlib&dom=statistics
↓
Tracked props: { page: "home", platform: "twitter", filter_lib: "plotly,matplotlib", filter_dom: "statistics" }
Note: Each filter category becomes a separate prop (filter_lib, filter_dom, etc.) to handle comma-separated values.
To see event properties in Plausible dashboard, you MUST register them as custom properties.
Go to: Plausible Dashboard → Site Settings → Custom Properties → Add Property
| Property | Description | Used By Events |
|---|---|---|
spec |
Plot specification ID | copy_code, download_image, catalog_rotate, external_link, internal_link, open_interactive, report_issue, tag_click, og_image_view |
library |
Library name (matplotlib, seaborn, etc.) | copy_code, download_image, external_link, internal_link, open_interactive, tab_toggle, og_image_view |
method |
Action method (card, image, tab, click, space, doubletap) | copy_code, random_filter |
page |
Page context (home, catalog, spec_overview, spec_detail) | copy_code, download_image, og_image_view |
platform |
Bot/platform name (twitter, whatsapp, teams, etc.) | og_image_view |
category |
Filter category (lib, spec, plot, data, dom, feat, dep, tech, pat, prep, style) | search, random_filter, filter_remove |
value |
Filter value | random_filter, filter_remove, tag_click |
query |
Search query text | search, search_no_results |
destination |
Link target (github, plausible, stats, compare, linkedin, mcp, legal) | external_link, internal_link |
tab |
Tab name (code, specification, implementation, quality) | tab_toggle |
action |
Toggle action (open, close) | tab_toggle |
size |
Grid size (normal, compact) | grid_resize |
param |
URL parameter name for tag | tag_click |
source |
Source page context | tag_click |
rating |
CWV rating (good, needs-improvement, poor) | LCP, CLS, INP |
filter_lib |
Library filter value (for og:image) | og_image_view |
filter_spec |
Specification filter value (for og:image) | og_image_view |
filter_plot |
Plot type filter value (for og:image) | og_image_view |
filter_data |
Data type filter value (for og:image) | og_image_view |
filter_dom |
Domain filter value (for og:image) | og_image_view |
filter_feat |
Features filter value (for og:image) | og_image_view |
filter_dep |
Dependencies filter value (for og:image) | og_image_view |
filter_tech |
Techniques filter value (for og:image) | og_image_view |
filter_pat |
Patterns filter value (for og:image) | og_image_view |
filter_prep |
Dataprep filter value (for og:image) | og_image_view |
filter_style |
Styling filter value (for og:image) | og_image_view |
Go to: Plausible Dashboard → Site Settings → Goals → Add Goal
| Goal Name | Type | Description |
|---|---|---|
copy_code |
Custom Event | Track code copies (primary conversion) |
download_image |
Custom Event | Track image downloads |
search |
Custom Event | Track successful searches |
search_no_results |
Custom Event | Track failed searches (content gaps) |
random_filter |
Custom Event | Track random filter usage |
filter_remove |
Custom Event | Track filter removal |
grid_resize |
Custom Event | Track view preference |
tab_toggle |
Custom Event | Track tab interactions |
external_link |
Custom Event | Track outbound clicks |
internal_link |
Custom Event | Track internal navigation links |
open_interactive |
Custom Event | Track interactive mode usage |
suggest_spec |
Custom Event | Track spec suggestion clicks |
report_issue |
Custom Event | Track issue report clicks |
tag_click |
Custom Event | Track tag filter clicks |
catalog_rotate |
Custom Event | Track catalog image rotation |
og_image_view |
Custom Event | Track og:image requests from social media bots |
LCP |
Custom Event | Largest Contentful Paint (Core Web Vital) |
CLS |
Custom Event | Cumulative Layout Shift (Core Web Vital) |
INP |
Custom Event | Interaction to Next Paint (Core Web Vital) |
Example funnel: Home → Spec → Copy
- Pageview
/(home) - Pageview
/{spec_id}/{library}(spec detail) copy_codeevent
Recommended custom widgets:
- Top Specs Copied:
copy_codebreakdown byspec - Popular Libraries:
copy_codebreakdown bylibrary - Copy Journey:
copy_codebreakdown bypage - Search Terms:
searchbreakdown byquery - Missing Content:
search_no_resultsbreakdown byquery - View Preference:
grid_resizebreakdown bysize - CWV Performance:
LCP/CLS/INPbreakdown byrating
The page property tracks where users perform actions to understand their journey:
User lands on pyplots.ai
│
├─→ Home (grid view)
│ └─→ copy_code { page: 'home' }
│
├─→ Spec Overview (/{spec_id})
│ └─→ copy_code { page: 'spec_overview' }
│ └─→ download_image { page: 'spec_overview' }
│
└─→ Spec Detail (/{spec_id}/{library})
└─→ copy_code { page: 'spec_detail' }
└─→ download_image { page: 'spec_detail' }
Q: Do users copy from search results or spec pages?
- Filter
copy_codebypageproperty home= direct from search/browsespec_overview= after viewing all implementationsspec_detail= after deep-diving into one library
Q: Which library implementations are most downloaded?
- Filter
download_imagebylibraryproperty - Filter by
pageto see if users download from overview or detail
| Event | Properties | Code Location |
|---|---|---|
copy_code |
spec, library, method, page |
ImageCard.tsx, SpecPage.tsx, SpecTabs.tsx |
download_image |
spec, library, page |
SpecPage.tsx |
search |
query, category |
FilterBar.tsx |
search_no_results |
query |
FilterBar.tsx |
random_filter |
category, value, method |
useFilterState.ts |
filter_remove |
category, value |
useFilterState.ts |
grid_resize |
size |
ToolbarActions.tsx |
tab_toggle |
action, tab, library |
SpecTabs.tsx |
tag_click |
param, value, source |
SpecTabs.tsx |
catalog_rotate |
spec |
CatalogPage.tsx |
open_interactive |
spec, library |
SpecOverview.tsx, SpecDetailView.tsx |
suggest_spec |
- | CatalogPage.tsx |
report_issue |
spec, library? |
SpecPage.tsx |
external_link |
destination, spec?, library? |
Footer.tsx, LegalPage.tsx |
internal_link |
destination, spec, library |
Footer.tsx |
LCP |
value, rating |
reportWebVitals.ts |
CLS |
value, rating |
reportWebVitals.ts |
INP |
value, rating |
reportWebVitals.ts |
og_image_view |
page, platform, spec?, library?, filter_*? |
api/analytics.py (server-side) |
Total: 19 client-side + 1 server-side = 20 events
Any valid specification ID (e.g., scatter-basic, heatmap-correlation, bar-grouped)
matplotlib | seaborn | plotly | bokeh | altair | plotnine | pygal | highcharts | letsplot
card # ImageCard copy button (home grid)
image # SpecPage image copy button
tab # SpecTabs code tab copy button
click # Random icon clicked
space # Spacebar pressed
doubletap # Mobile double-tap
home # HomePage grid view (client) or og:image home endpoint (server)
catalog # CatalogPage (server og:image only)
spec_overview # SpecPage showing all libraries
spec_detail # SpecPage showing single library
# Social Media
twitter | facebook | linkedin | pinterest | reddit | tumblr | mastodon
# Messaging Apps
slack | discord | telegram | whatsapp | signal | viber | skype | teams | snapchat
# Search Engines
google | bing | yandex | duckduckgo | baidu | apple
# Link Preview Services
embedly | quora | outbrain | rogerbot | showyoubot
# Fallback
unknown
# Spec-level (WHAT is visualized)
lib # library filter
spec # specification filter
plot # plot_type filter
data # data_type filter
dom # domain filter
feat # features filter
# Impl-level (HOW it is implemented)
dep # dependencies filter
tech # techniques filter
pat # patterns filter
prep # dataprep filter
style # styling filter
code # Python code tab
specification # Spec details tab
implementation # AI implementation review tab
quality # Quality score breakdown tab
github # GitHub repository link (Footer)
stats # Plausible stats dashboard link (Footer)
linkedin # LinkedIn profile link (Footer, LegalPage)
x # X/Twitter profile link (LegalPage)
github_personal # Personal GitHub link (LegalPage)
mcp # MCP documentation page (internal link, Footer)
legal # Legal page (internal link, Footer)
normal # Larger cards (1-3 columns)
compact # Smaller cards (2-6 columns)
good # Within recommended thresholds
needs-improvement # Between good and poor thresholds
poor # Exceeds poor threshold
- Plausible setup:
app/index.html(lines 59-68) - Analytics hook:
app/src/hooks/useAnalytics.ts - Pageview building:
buildPlausibleUrl()in useAnalytics.ts - Core Web Vitals:
app/src/analytics/reportWebVitals.ts - Event tracking: Passed via
onTrackEventprop throughout component tree
Development: Tracking disabled (not on pyplots.ai domain)
Production testing:
- Open https://pyplots.ai
- Open browser console
- Check for Plausible script load
- Trigger events and verify in Plausible dashboard (may take 1-2 min)
Debug mode:
// In browser console
window.plausible = function(...args) { console.log('Plausible:', args); };- Plausible script loaded
- Manual pageview tracking
- Filter-based URL generation
- Code copy events with journey tracking (
copy_code+page) - Download tracking (
download_image+page) - Search events (
search,search_no_results) - Random filter events (
random_filter) - Filter removal tracking (
filter_remove) - Grid size toggle tracking (
grid_resize) - Tab interaction events (
tab_toggle) - Tag click events (
tag_click) - External link events (
external_link) - Internal link events (
internal_link) - Feature link events (
open_interactive,suggest_spec,report_issue) - Catalog rotation (
catalog_rotate) - Core Web Vitals tracking (
LCP,CLS,INP) - Server-side og:image tracking (
og_image_view) with platform detection
- Register all custom properties (see table above, including
rating,action,param,source,platform,filter_*) - Create goals for key events (including
LCP,CLS,INP) - Set up funnels (optional)
- Create custom dashboard widgets (optional)
Last Updated: 2026-03-10 Status: Production-ready with full journey tracking, Core Web Vitals, and server-side og:image analytics