Overview
Chartbeat offers multiple deployment methods to accommodate different technical architectures and publishing platforms. The choice of implementation method depends on your CMS, development workflow, consent management requirements, and technical capabilities. This guide covers all major deployment approaches with detailed code examples and best practices.
Deployment Decision Matrix
| Method | Best For | Pros | Cons |
|---|---|---|---|
| Tag Manager | Most organizations with GTM/Tealium | Easy updates, no code deploys | TMS dependency, slight delay |
| CMS Template | WordPress, Drupal, custom CMS | Fast loading, reliable | Requires CMS updates |
| Hard-Coded | Static sites, simple setups | Maximum control, fastest | Manual updates needed |
| Mobile SDK | Native iOS/Android apps | Native performance, offline support | Separate implementation |
| Server-Side | AMP, SSR frameworks | SEO benefits, bot filtering | Limited engagement tracking |
Deployment Strategy
Primary Deployment Method Selection
Recommended Approach: Tag Manager Integration
- Inject Chartbeat via Google Tag Manager, Tealium, or Adobe Launch
- Populate configuration from data layer variables
- Enable dynamic metadata updates without code deployments
- Centralize consent management and script sequencing
- Simplify cross-team collaboration (marketing, analytics, engineering)
Alternative: CMS Template Integration
- Embed directly in CMS templates (WordPress, Drupal, custom platforms)
- Ensure script loads after consent if required
- Leverage CMS metadata for automatic population of sections, authors, titles
- Ideal when tag manager adds unacceptable latency
- Provides maximum performance and reliability
When to Use Hard-Coded Implementation
- Static sites or simple HTML deployments
- Maximum performance requirements
- No tag manager or CMS infrastructure
- Full developer control over script loading and configuration
Key Implementation Considerations
Consent Management
- Load Chartbeat only after user consent in GDPR/CCPA jurisdictions
- Gate heartbeat pings until opt-in is confirmed
- Implement cookie-less tracking if required (
noCookies: true) - Coordinate with OneTrust, Cookiebot, or custom consent solutions
Script Loading Sequence
- Load Chartbeat early for accurate engagement timing
- Ensure it fires after consent but before user interaction
- Avoid race conditions with other analytics tools
- Prevent multiple script inclusions on the same page
Heartbeat Configuration
- Default interval: 15 seconds (recommended by Chartbeat)
- Adjust for SPA frameworks to maintain timers across route changes
- Consider network conditions for mobile users
- Monitor ping frequency to avoid excessive API calls
Tag Manager Deployment
Google Tag Manager Implementation
Step 1: Create Data Layer Variables
Create these variables in GTM to populate Chartbeat configuration:
| Variable Name | Type | Data Layer Variable | Example Value |
|---|---|---|---|
chartbeat.uid |
Data Layer Variable | chartbeat_uid |
12345 |
chartbeat.domain |
Data Layer Variable | chartbeat_domain |
example.com |
chartbeat.sections |
Data Layer Variable | chartbeat_sections |
News,Politics |
chartbeat.authors |
Data Layer Variable | chartbeat_authors |
Jane Smith |
chartbeat.title |
Data Layer Variable | page_title |
Article Title |
chartbeat.path |
Data Layer Variable | page_path |
/news/politics/article |
Step 2: Configure Custom HTML Tag
Create a Custom HTML tag with this configuration:
<script type="text/javascript">
var _sf_async_config = {
uid: {{chartbeat.uid}},
domain: '{{chartbeat.domain}}',
useCanonical: true,
useCanonicalDomain: true,
sections: '{{chartbeat.sections}}',
authors: '{{chartbeat.authors}}',
title: '{{chartbeat.title}}',
path: '{{chartbeat.path}}'
};
(function() {
function loadChartbeat() {
var e = document.createElement('script');
var n = document.getElementsByTagName('script')[0];
e.type = 'text/javascript';
e.async = true;
e.src = '//static.chartbeat.com/js/chartbeat.js';
n.parentNode.insertBefore(e, n);
}
loadChartbeat();
})();
</script>
Step 3: Set Trigger Conditions
- Trigger Type: Page View - DOM Ready
- Fire On: All Pages (or specific page types)
- Exceptions: Add consent-related conditions if needed
Step 4: Set Tag Sequencing
- Fire After: Consent management tag completes
- Fire Before: Other analytics tags that depend on Chartbeat
- Priority: Set to higher priority than general analytics tags
Step 5: SPA Virtual Page View Configuration
For single-page applications, create an additional Custom HTML tag:
<script type="text/javascript">
(function() {
// Wait for pSUPERFLY to be available
var checkChartbeat = setInterval(function() {
if (typeof pSUPERFLY !== 'undefined') {
clearInterval(checkChartbeat);
// Update configuration with new page data
_sf_async_config.sections = '{{chartbeat.sections}}';
_sf_async_config.authors = '{{chartbeat.authors}}';
_sf_async_config.title = '{{chartbeat.title}}';
_sf_async_config.path = '{{chartbeat.path}}';
// Trigger virtual page view
pSUPERFLY.virtualPage({
sections: '{{chartbeat.sections}}',
authors: '{{chartbeat.authors}}',
title: '{{chartbeat.title}}',
path: '{{chartbeat.path}}'
});
}
}, 100);
})();
</script>
- Trigger Type: Custom Event -
virtualPageview(or your SPA route change event) - Fire On: Route change events from your SPA framework
Tealium iQ Implementation
Load Rule Configuration
// Tealium utag.js extension
if (typeof _sf_async_config === 'undefined') {
var _sf_async_config = {};
}
_sf_async_config.uid = utag_data.chartbeat_uid || 12345;
_sf_async_config.domain = utag_data.chartbeat_domain || 'example.com';
_sf_async_config.sections = utag_data.chartbeat_sections || '';
_sf_async_config.authors = utag_data.chartbeat_authors || '';
_sf_async_config.title = utag_data.page_title || document.title;
_sf_async_config.path = utag_data.page_path || window.location.pathname;
_sf_async_config.useCanonical = true;
(function() {
var e = document.createElement('script');
e.async = true;
e.src = '//static.chartbeat.com/js/chartbeat.js';
document.body.appendChild(e);
})();
Direct Embed or Hard-Coded Scripts
Standard Implementation
Place this code in your HTML template, ideally near the end of the <body> tag:
<!DOCTYPE html>
<html>
<head>
<title>Your Page Title</title>
</head>
<body>
<!-- Your page content -->
<!-- Chartbeat Configuration -->
<script type="text/javascript">
var _sf_async_config = {
uid: 12345, // Required: Your Chartbeat UID
domain: 'example.com', // Required: Your domain
useCanonical: true, // Recommended: Use canonical URL
useCanonicalDomain: true, // Recommended: Use canonical domain
sections: 'News,Politics', // Section taxonomy
authors: 'Jane Smith', // Author attribution
title: 'Article Headline Goes Here', // Page title
path: '/news/politics/article-slug', // Canonical path
// Optional configuration
noCookies: false, // Set to true for cookie-less tracking
flickerControl: false, // A/B test flicker control
mabAllow: true // Enable headline testing
};
</script>
<!-- Chartbeat Script Loader -->
<script type="text/javascript">
(function() {
function loadChartbeat() {
window._sf_endpt = (new Date()).getTime();
var e = document.createElement('script');
var n = document.getElementsByTagName('script')[0];
e.type = 'text/javascript';
e.async = true;
e.src = '//static.chartbeat.com/js/chartbeat.js';
n.parentNode.insertBefore(e, n);
}
if (window.addEventListener) {
window.addEventListener('load', loadChartbeat, false);
} else if (window.attachEvent) {
window.attachEvent('onload', loadChartbeat);
} else {
loadChartbeat();
}
})();
</script>
</body>
</html>
CMS-Specific Examples
WordPress Implementation
Add to your theme's footer.php or use a plugin like Insert Headers and Footers:
<!-- Chartbeat Analytics -->
<script type="text/javascript">
var _sf_async_config = {
uid: <?php echo get_option('chartbeat_uid', 12345); ?>,
domain: '<?php echo $_SERVER['HTTP_HOST']; ?>',
useCanonical: true,
useCanonicalDomain: true,
sections: '<?php echo get_the_category()[0]->name; ?>',
authors: '<?php echo get_the_author(); ?>',
title: '<?php echo get_the_title(); ?>',
path: '<?php echo parse_url(get_permalink(), PHP_URL_PATH); ?>'
};
</script>
<script type="text/javascript" src="//static.chartbeat.com/js/chartbeat.js"></script>
Create a Chartbeat component:
// components/Chartbeat.js
import { useEffect } from 'react';
import { useRouter } from 'next/router';
const Chartbeat = ({ uid, domain, sections, authors, title }) => {
const router = useRouter();
useEffect(() => {
// Initialize Chartbeat
window._sf_async_config = {
uid: uid,
domain: domain,
useCanonical: true,
useCanonicalDomain: true,
sections: sections,
authors: authors,
title: title,
path: router.asPath
};
// Load script
const script = document.createElement('script');
script.src = '//static.chartbeat.com/js/chartbeat.js';
script.async = true;
document.body.appendChild(script);
// Handle route changes
const handleRouteChange = (url) => {
if (typeof window.pSUPERFLY !== 'undefined') {
window.pSUPERFLY.virtualPage({
sections: sections,
authors: authors,
title: document.title,
path: url
});
}
};
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router, uid, domain, sections, authors, title]);
return null;
};
export default Chartbeat;
Mobile & OTT SDKs
iOS SDK Implementation
Installation via CocoaPods
# Podfile
pod 'Chartbeat', '~> 5.0'
Swift Configuration
import Chartbeat
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize Chartbeat
Chartbeat.sharedTracker().startTracker(
withAccountId: "12345",
domain: "example.com"
)
return true
}
}
// Track page views
Chartbeat.sharedTracker().trackView(
viewId: "article-123",
viewTitle: "Article Headline",
authors: ["Jane Smith"],
sections: ["News", "Politics"]
)
Android SDK Implementation
Gradle Dependency
dependencies {
implementation 'com.chartbeat:chartbeat-android:5.0.0'
}
Kotlin Configuration
import com.chartbeat.androidsdk.Tracker
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize Chartbeat
val tracker = Tracker.getInstance()
tracker.setAccountId("12345")
tracker.setDomain("example.com")
tracker.startTracker(this)
// Track page view
tracker.trackView(
viewId = "article-123",
title = "Article Headline",
authors = listOf("Jane Smith"),
sections = listOf("News", "Politics")
)
}
}
WebView Integration
For hybrid apps using WebViews, inject the standard JavaScript implementation:
// Inject into WebView
String chartbeatScript =
"var _sf_async_config = {" +
" uid: 12345," +
" domain: 'example.com'," +
" sections: 'News,Politics'," +
" authors: 'Jane Smith'" +
"};" +
"(function(){" +
" var e = document.createElement('script');" +
" e.src = '//static.chartbeat.com/js/chartbeat.js';" +
" e.async = true;" +
" document.body.appendChild(e);" +
"})();";
webView.evaluateJavascript(chartbeatScript, null);
Server-Side Collection
AMP (Accelerated Mobile Pages)
AMP pages require a special analytics configuration:
<amp-analytics type="chartbeat">
<script type="application/json">
{
"vars": {
"uid": "12345",
"domain": "example.com",
"sections": "News,Politics",
"authors": "Jane Smith",
"title": "Article Headline"
}
}
</script>
</amp-analytics>
Server-Side Rendering (SSR)
For Next.js, Nuxt, or other SSR frameworks, generate the config server-side:
// pages/_document.js (Next.js)
import Document, { Html, Head, Main, NextScript } from 'next/document';
class MyDocument extends Document {
render() {
const { pageProps } = this.props.__NEXT_DATA__.props;
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
<script
dangerouslySetInnerHTML={{
__html: `
var _sf_async_config = {
uid: 12345,
domain: 'example.com',
sections: '${pageProps.sections || ''}',
authors: '${pageProps.authors || ''}',
title: '${pageProps.title || ''}',
path: '${pageProps.path || ''}'
};
`
}}
/>
<script async src="//static.chartbeat.com/js/chartbeat.js" />
</body>
</Html>
);
}
}
export default MyDocument;
Validation Checklist
Pre-Deployment Validation
- UID and domain configured correctly
- Script loads asynchronously without blocking page render
- Configuration variables populated from correct data sources
- Consent management integration working properly
- No JavaScript errors in browser console
- Script loads from Chartbeat CDN successfully
Post-Deployment Validation
Real-Time Dashboard Checks
- Concurrent user count shows test traffic
- Engagement timer increments properly
- Section and author metadata appears correctly
- Page titles and paths match expected values
- Geographic data shows correct location
Network Analysis
- Initial ping sends complete configuration
- Heartbeat pings occur at 15-second intervals
- Payload size is reasonable (< 2KB per ping)
- No duplicate pings or infinite loops
- Proper HTTP status codes (200 OK)
Cross-Browser Testing
- Chrome (latest and previous version)
- Firefox (latest and previous version)
- Safari (latest and previous version)
- Edge (latest version)
- Mobile browsers (iOS Safari, Android Chrome)
SPA-Specific Validation (if applicable)
- Virtual page views fire on route changes
- Metadata updates with new page context
- Engagement timer continues (doesn't reset)
- No duplicate tracker initialization
- Browser back/forward navigation works correctly
Performance Validation
| Metric | Target | How to Measure |
|---|---|---|
| Script load time | < 500ms | Network tab in DevTools |
| Time to first ping | < 2s | Network tab in DevTools |
| Page load impact | < 100ms | Lighthouse performance audit |
| Heartbeat interval | 15s ± 1s | Monitor ping timing in Network tab |
| Payload size | < 2KB | Network tab, ping request size |
Troubleshooting Common Issues
| Issue | Symptoms | Diagnosis | Resolution |
|---|---|---|---|
| Script not loading | No network requests to chartbeat.com | Check browser console for errors | Verify script URL, check CSP policies, ensure no ad blockers |
| Missing configuration | Pings missing metadata fields | Inspect _sf_async_config in console |
Check data layer values, verify variable mapping |
| Duplicate tracking | Multiple pings per heartbeat interval | Check for duplicate script includes | Remove redundant implementations, check TMS tag firing |
| Consent blocking | No pings before consent | Expected behavior in GDPR regions | Verify consent integration, check for errors in consent callback |
| SPA tracking broken | No virtual page views on navigation | Monitor pSUPERFLY object availability |
Implement virtualPage calls, check router integration |
| Performance impact | Slow page loads | Run Lighthouse audit | Verify async loading, check ping frequency, optimize payload |
| Mobile SDK errors | Crashes or missing data | Check device logs | Verify SDK version compatibility, check initialization |
| Cross-domain issues | Referrer data incorrect | Check referrer values in pings | Implement proper referrer passing, check redirect chains |
Configuration Recommendations
SPA Integration: Use Chartbeat's pSUB() function to send virtual pageviews on route changes. Call pSUB({sections: 'new-section', authors: 'Author Name', path: '/new-path', title: 'New Title'}) after each navigation. Engagement timers reset automatically on pSUB() calls.
Heartbeat Interval: Keep the default 15-second heartbeat interval unless you have a specific reason to change it. Shorter intervals increase network requests; longer intervals reduce engagement accuracy. The 15-second default balances data granularity with performance.
Feature Enablement: Enable headline testing (mabAllow: true in config) if your editorial team wants A/B test headlines — Chartbeat will automatically rotate headlines and measure engagement. Enable the recirculation module only if you use Chartbeat's content recommendation widget. Video tracking requires the Video module and a compatible player (JW Player, Brightcove, or HTML5 <video>).
Best Practices
- Load Early, Load Async: Place Chartbeat high in the page but always load asynchronously
- Validate Metadata: Ensure sections, authors, and titles are accurate and consistent
- Monitor Performance: Regularly audit script impact on page load times
- Test Thoroughly: Validate across browsers, devices, and network conditions
- Plan for Consent: Always respect user privacy and comply with regional regulations
- Version Control: Track changes to implementation and configuration
- Monitor Data Quality: Regularly audit Real-Time dashboard for anomalies