Grav is a modern flat-file CMS that stores content in Markdown files. It offers a flexible plugin architecture and Twig templating for implementing analytics integrations.
Available Integrations
Analytics Platforms
Tag Management
- GTM plugin installation
- Data layer configuration via Twig
- Custom event triggers
- Page type tracking
Marketing Pixels
- Pixel implementation options
- Event tracking configuration
- Content tracking for articles
- Privacy-compliant setup
Grav-Specific Integration Considerations
Flat-File Architecture
Grav's unique content storage:
- Markdown Files: Content stored as .md files
- YAML Frontmatter: Metadata in file headers
- Folder Structure: URL structure matches folders
- No Database: Fast and portable
Twig Template Integration
Implement tracking via Twig templates:
{# In templates/partials/analytics.html.twig #}
{% if config.plugins.google_analytics.tracking_id %}
<script async src="https://www.googletagmanager.com/gtag/js?id={{ config.plugins.google_analytics.tracking_id }}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{{ config.plugins.google_analytics.tracking_id }}', {
page_title: '{{ page.title }}',
page_path: '{{ page.url }}',
content_type: '{{ page.template }}',
{% if page.header.author %}
author: '{{ page.header.author }}',
{% endif %}
{% if page.header.category %}
category: '{{ page.header.category|join(",") }}'
{% endif %}
});
</script>
{% endif %}
Page Frontmatter Tracking
Leverage Grav's frontmatter for analytics:
---
title: 'My Article'
author: 'John Doe'
category:
- Technology
- Tutorials
taxonomy:
tag: [grav, cms, tutorial]
analytics:
track_scroll: true
content_group: 'blog'
---
{# Access frontmatter in templates #}
<script>
gtag('event', 'page_view', {
page_title: '{{ page.title }}',
author: '{{ page.header.author }}',
categories: '{{ page.header.category|join(",") }}',
tags: '{{ page.taxonomy.tag|join(",") }}',
content_group: '{{ page.header.analytics.content_group|default("general") }}'
});
</script>
Plugin Event Hooks
Create custom tracking plugins:
<?php
// user/plugins/custom-analytics/custom-analytics.php
namespace Grav\Plugin;
use Grav\Common\Plugin;
use RocketTheme\Toolbox\Event\Event;
class CustomAnalyticsPlugin extends Plugin
{
public static function getSubscribedEvents()
{
return [
'onPageInitialized' => ['onPageInitialized', 0],
'onTwigSiteVariables' => ['onTwigSiteVariables', 0]
];
}
public function onTwigSiteVariables(Event $e)
{
$page = $this->grav['page'];
$this->grav['twig']->twig_vars['analytics_data'] = [
'page_id' => $page->id(),
'page_route' => $page->route(),
'page_template' => $page->template(),
'page_modified' => $page->modified(),
'is_blog_post' => $page->template() === 'item'
];
}
}
Blog/Collection Tracking
Track Grav blog and collections:
{# For blog listing pages #}
{% for post in page.collection %}
<article data-analytics-id="{{ post.id }}" data-analytics-title="{{ post.title }}">
<a href="{{ post.url }}" post.id }}', '{{ post.title }}')">
{{ post.title }}
</a>
</article>
{% endfor %}
<script>
function trackBlogClick(postId, title) {
gtag('event', 'blog_post_click', {
post_id: postId,
post_title: title,
list_page: '{{ page.title }}'
});
}
</script>
Modular Page Tracking
Track Grav's modular pages:
{# In modular template #}
{% for module in page.collection() %}
<section
class="modular-{{ module.template }}"
data-module-type="{{ module.template }}"
data-module-title="{{ module.title }}"
>
{% include 'modular/' ~ module.template ~ '.html.twig' %}
</section>
{% endfor %}
<script>
// Track modular section visibility
document.querySelectorAll('[data-module-type]').forEach(section => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
gtag('event', 'module_view', {
module_type: entry.target.dataset.moduleType,
module_title: entry.target.dataset.moduleTitle
});
}
});
}, { threshold: 0.5 });
observer.observe(section);
});
</script>
Integration Best Practices
1. Use Page IDs for Tracking
Grav pages have stable IDs based on folder path:
gtag('event', 'content_view', {
content_id: '{{ page.id }}', {# Stable folder-based ID #}
content_slug: '{{ page.slug }}', {# May change #}
content_route: '{{ page.route }}'
});
2. Track Taxonomy
Leverage Grav's taxonomy system:
gtag('event', 'page_view', {
page_id: '{{ page.id }}',
categories: '{{ page.taxonomy.category|join(",") }}',
tags: '{{ page.taxonomy.tag|join(",") }}',
authors: '{{ page.taxonomy.author|join(",") }}'
});
3. Handle Multi-Language Sites
For Grav's multi-language support:
gtag('event', 'page_view', {
page_id: '{{ page.id }}',
page_language: '{{ grav.language.getActive }}',
default_language: '{{ grav.language.getDefault }}',
available_languages: '{{ grav.language.getLanguages|join(",") }}'
});
4. Admin Exclusion
Exclude admin sessions from analytics:
{% if not admin %}
{# Include analytics code #}
{% include 'partials/analytics.html.twig' %}
{% endif %}
5. Cache Considerations
Handle Grav's caching with analytics:
# In system/config/system.yaml
cache:
enabled: true
# Ensure analytics scripts are not cached with stale data
# Use JavaScript to fetch dynamic data
Plugin Configuration
Installing Analytics Plugins
# Install via GPM (Grav Package Manager)
bin/gpm install google-analytics
# Or download and place in user/plugins/
Plugin Configuration
# user/config/plugins/google-analytics.yaml
enabled: true
tracking_id: G-XXXXXXXXXX
anonymize_ip: true
blocked_ips:
- 127.0.0.1
- 192.168.1.0/24
track_logged_in: false
debug: false
Custom Event Configuration
# user/config/plugins/custom-analytics.yaml
enabled: true
events:
track_downloads: true
track_outbound_links: true
track_scroll_depth: true
scroll_milestones: [25, 50, 75, 100]
Testing Integrations
Twig Template Testing:
- Verify variable output
- Test conditional logic
- Check frontmatter access
Plugin Testing:
- Test event hooks
- Verify data layer output
- Check caching behavior
Multi-Language Testing:
- Test language switching
- Verify locale tracking
- Check translated content
Next Steps
Choose your integration to get started:
Additional Resources
Grav Documentation: