This guide covers tracking Carrd-specific interactions with Google Analytics 4 (GA4), including forms, button clicks, scroll depth, and outbound links on your one-page Carrd sites.
Prerequisites
- GA4 installed on your Carrd site via embed method
- Carrd Pro account (Pro Lite, Pro Standard, or Pro Plus)
- Published Carrd site (events only fire on published sites, not preview)
- Basic JavaScript knowledge for implementing custom event tracking
Carrd Form Tracking
Carrd's built-in forms are the primary conversion point on most sites. Track form submissions effectively.
Basic Form Submission Tracking
Add this code in a separate Embed element placed after your form:
- Add a new Embed element immediately after your form
- Select Code as the embed type
- Paste this code:
<script>
document.addEventListener('DOMContentLoaded', function() {
// Find all Carrd forms
var forms = document.querySelectorAll('form');
forms.forEach(function(form) {
form.addEventListener('submit', function(e) {
// Get form action to identify which form
var formAction = form.getAttribute('action') || 'unknown';
var formId = form.getAttribute('id') || 'unknown';
// Send form submission event to GA4
gtag('event', 'form_submit', {
'form_id': formId,
'form_location': window.location.href,
'event_category': 'engagement'
});
});
});
});
</script>
Track Successful Form Submissions
Carrd forms redirect or show a success message. Use sendBeacon to ensure tracking fires before redirect:
<script>
document.addEventListener('DOMContentLoaded', function() {
var forms = document.querySelectorAll('form');
forms.forEach(function(form) {
form.addEventListener('submit', function(e) {
var formId = form.getAttribute('id') || 'contact_form';
// Use sendBeacon for guaranteed delivery before redirect
gtag('event', 'generate_lead', {
'event_category': 'form',
'form_id': formId,
'transport_type': 'beacon'
});
});
});
});
</script>
Track Specific Form Fields (Without PII)
Track form interactions without capturing personal information:
<script>
document.addEventListener('DOMContentLoaded', function() {
var forms = document.querySelectorAll('form');
forms.forEach(function(form) {
form.addEventListener('submit', function(e) {
// Count filled fields (don't capture values)
var inputs = form.querySelectorAll('input, textarea, select');
var filledFields = 0;
inputs.forEach(function(input) {
if (input.value && input.value.trim() !== '') {
filledFields++;
}
});
// Track form completion level
gtag('event', 'form_submit', {
'form_id': form.getAttribute('id') || 'unknown',
'fields_filled': filledFields,
'total_fields': inputs.length,
'completion_rate': Math.round((filledFields / inputs.length) * 100)
});
});
});
});
</script>
Privacy note: Do NOT track actual form field values (emails, names, phone numbers) - this violates privacy regulations and GA4 terms of service.
Track Form Field Focus
Measure form engagement even without submission:
<script>
document.addEventListener('DOMContentLoaded', function() {
var forms = document.querySelectorAll('form');
forms.forEach(function(form) {
var formInteracted = false;
var inputs = form.querySelectorAll('input, textarea, select');
inputs.forEach(function(input) {
input.addEventListener('focus', function() {
if (!formInteracted) {
formInteracted = true;
gtag('event', 'form_start', {
'form_id': form.getAttribute('id') || 'unknown',
'event_category': 'engagement'
});
}
});
});
});
});
</script>
Button and Link Tracking
Track specific buttons and calls-to-action on your Carrd site.
Track All Buttons
<script>
document.addEventListener('DOMContentLoaded', function() {
// Track all buttons
var buttons = document.querySelectorAll('button, .button, [role="button"]');
buttons.forEach(function(button) {
button.addEventListener('click', function() {
var buttonText = this.textContent.trim();
var buttonHref = this.getAttribute('href') || 'no_link';
gtag('event', 'button_click', {
'button_text': buttonText,
'button_url': buttonHref,
'page_location': window.location.href
});
});
});
});
</script>
Track Specific Buttons with Data Attributes
Add custom attributes to track specific buttons:
In Carrd Editor:
- Select your button element
- Click Settings (gear icon)
- Add custom attributes in the Attributes section
- Add:
data-event="cta_click"ordata-event="signup_button"
Tracking code:
<script>
document.addEventListener('DOMContentLoaded', function() {
var trackableElements = document.querySelectorAll('[data-event]');
trackableElements.forEach(function(element) {
element.addEventListener('click', function() {
var eventName = this.getAttribute('data-event');
var elementText = this.textContent.trim();
var elementHref = this.getAttribute('href') || 'no_link';
gtag('event', eventName, {
'link_text': elementText,
'link_url': elementHref,
'event_category': 'engagement'
});
});
});
});
</script>
Track Outbound Links
Automatically track clicks to external websites:
<script>
document.addEventListener('DOMContentLoaded', function() {
var links = document.querySelectorAll('a');
links.forEach(function(link) {
link.addEventListener('click', function(e) {
var href = this.getAttribute('href');
var currentDomain = window.location.hostname;
// Check if link is external
if (href && href.startsWith('http') && !href.includes(currentDomain)) {
gtag('event', 'click', {
'event_category': 'outbound',
'event_label': href,
'transport_type': 'beacon'
});
}
});
});
});
</script>
Track Social Media Links
Track clicks to social profiles (common on Carrd link-in-bio sites):
<script>
document.addEventListener('DOMContentLoaded', function() {
// Define social media domains
var socialDomains = [
'twitter.com', 'x.com',
'instagram.com',
'facebook.com',
'linkedin.com',
'tiktok.com',
'youtube.com',
'github.com',
'dribbble.com',
'behance.net'
];
var links = document.querySelectorAll('a');
links.forEach(function(link) {
link.addEventListener('click', function() {
var href = this.getAttribute('href') || '';
socialDomains.forEach(function(domain) {
if (href.includes(domain)) {
var platform = domain.split('.')[0];
gtag('event', 'social_link_click', {
'platform': platform,
'link_url': href,
'event_category': 'social'
});
}
});
});
});
});
</script>
Scroll Depth Tracking
Since Carrd sites are one-page layouts, scroll depth is a crucial engagement metric.
Basic Scroll Depth Tracking
Track when users scroll to 25%, 50%, 75%, and 100% of page:
<script>
(function() {
var scrollMarks = [25, 50, 75, 100];
var trackedMarks = [];
function checkScrollDepth() {
var windowHeight = window.innerHeight;
var documentHeight = document.documentElement.scrollHeight;
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
var scrollPercent = Math.round((scrollTop / (documentHeight - windowHeight)) * 100);
scrollMarks.forEach(function(mark) {
if (scrollPercent >= mark && trackedMarks.indexOf(mark) === -1) {
trackedMarks.push(mark);
gtag('event', 'scroll', {
'percent_scrolled': mark,
'event_category': 'engagement'
});
}
});
}
// Throttle scroll events for performance
var scrollTimeout;
window.addEventListener('scroll', function() {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(checkScrollDepth, 100);
});
})();
</script>
Section-Based Scroll Tracking
Track when users scroll to specific sections (useful for portfolio or multi-section landing pages):
<script>
document.addEventListener('DOMContentLoaded', function() {
// Track when sections come into view
var sections = document.querySelectorAll('section, .section');
var trackedSections = [];
function checkSectionVisibility() {
sections.forEach(function(section) {
var sectionId = section.getAttribute('id') || section.className;
if (trackedSections.indexOf(sectionId) === -1) {
var rect = section.getBoundingClientRect();
var windowHeight = window.innerHeight;
// Check if section is at least 50% visible
if (rect.top < windowHeight * 0.5 && rect.bottom > 0) {
trackedSections.push(sectionId);
gtag('event', 'section_view', {
'section_id': sectionId,
'event_category': 'engagement'
});
}
}
});
}
var scrollTimeout;
window.addEventListener('scroll', function() {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(checkSectionVisibility, 200);
});
// Check on load
checkSectionVisibility();
});
</script>
File Download Tracking
Track downloads of PDFs, resumes, portfolios, etc.:
<script>
document.addEventListener('DOMContentLoaded', function() {
var downloadLinks = document.querySelectorAll('a[href$=".pdf"], a[href$=".zip"], a[href$=".doc"], a[href$=".docx"], a[href$=".ppt"], a[href$=".pptx"]');
downloadLinks.forEach(function(link) {
link.addEventListener('click', function() {
var fileUrl = this.getAttribute('href');
var fileName = fileUrl.split('/').pop();
var fileExtension = fileName.split('.').pop();
gtag('event', 'file_download', {
'file_name': fileName,
'file_extension': fileExtension,
'file_url': fileUrl,
'link_text': this.textContent.trim()
});
});
});
});
</script>
Email Link Tracking
Track clicks on email addresses (common on contact pages):
<script>
document.addEventListener('DOMContentLoaded', function() {
var emailLinks = document.querySelectorAll('a[href^="mailto:"]');
emailLinks.forEach(function(link) {
link.addEventListener('click', function() {
var email = this.getAttribute('href').replace('mailto:', '');
gtag('event', 'email_click', {
'event_category': 'contact',
'event_label': 'email_link_clicked',
'transport_type': 'beacon'
});
});
});
});
</script>
Privacy note: Don't send the actual email address to GA4.
Phone Link Tracking
Track clicks on phone numbers:
<script>
document.addEventListener('DOMContentLoaded', function() {
var phoneLinks = document.querySelectorAll('a[href^="tel:"]');
phoneLinks.forEach(function(link) {
link.addEventListener('click', function() {
gtag('event', 'phone_click', {
'event_category': 'contact',
'event_label': 'phone_link_clicked',
'transport_type': 'beacon'
});
});
});
});
</script>
Video Tracking
Track Embedded YouTube Videos
<script>
// Load YouTube IFrame API
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// Track video events
function onYouTubeIframeAPIReady() {
var videos = document.querySelectorAll('iframe[src*="youtube.com"], iframe[src*="youtu.be"]');
videos.forEach(function(video, index) {
new YT.Player(video, {
events: {
'onStateChange': function(event) {
var videoUrl = event.target.getVideoUrl();
if (event.data == YT.PlayerState.PLAYING) {
gtag('event', 'video_start', {
'video_url': videoUrl,
'video_provider': 'youtube',
'event_category': 'video'
});
} else if (event.data == YT.PlayerState.ENDED) {
gtag('event', 'video_complete', {
'video_url': videoUrl,
'video_provider': 'youtube',
'event_category': 'video'
});
}
}
}
});
});
}
</script>
Track Vimeo Videos
<script>
// Load Vimeo Player API
var script = document.createElement('script');
script.src = 'https://player.vimeo.com/api/player.js';
document.head.appendChild(script);
script.onload = function() {
var vimeoVideos = document.querySelectorAll('iframe[src*="vimeo.com"]');
vimeoVideos.forEach(function(video) {
var player = new Vimeo.Player(video);
player.on('play', function() {
player.getVideoTitle().then(function(title) {
gtag('event', 'video_start', {
'video_title': title,
'video_provider': 'vimeo',
'event_category': 'video'
});
});
});
player.on('ended', function() {
player.getVideoTitle().then(function(title) {
gtag('event', 'video_complete', {
'video_title': title,
'video_provider': 'vimeo',
'event_category': 'video'
});
});
});
});
};
</script>
Time on Page Tracking
Track engaged time on your Carrd page:
<script>
(function() {
var startTime = new Date().getTime();
var engaged = true;
var engagedTime = 0;
var lastCheck = startTime;
// Track active engagement
function trackEngagement() {
if (engaged) {
var now = new Date().getTime();
engagedTime += (now - lastCheck);
lastCheck = now;
}
}
// User is engaged
window.addEventListener('mousemove', function() { engaged = true; });
window.addEventListener('scroll', function() { engaged = true; });
window.addEventListener('keydown', function() { engaged = true; });
// User is disengaged
window.addEventListener('blur', function() { engaged = false; });
// Track every 5 seconds
setInterval(trackEngagement, 5000);
// Send on page unload
window.addEventListener('beforeunload', function() {
trackEngagement();
var totalSeconds = Math.round(engagedTime / 1000);
if (totalSeconds > 5) { // Only track if engaged for more than 5 seconds
gtag('event', 'engaged_time', {
'value': totalSeconds,
'event_category': 'engagement',
'transport_type': 'beacon'
});
}
});
})();
</script>
Custom Embed Tracking
If you embed third-party widgets (Calendly, Gumroad, etc.), track interactions:
Calendly Event Tracking
<script>
window.addEventListener('message', function(e) {
if (e.data.event && e.data.event.indexOf('calendly') === 0) {
gtag('event', e.data.event, {
'event_category': 'calendly',
'event_label': e.data.event
});
}
});
</script>
Gumroad Purchase Tracking
<script>
window.addEventListener('message', function(e) {
if (e.data && e.data.action === 'gumroad:purchase') {
gtag('event', 'purchase', {
'event_category': 'ecommerce',
'event_label': 'gumroad',
'currency': 'USD',
'value': e.data.amount || 0
});
}
});
</script>
Custom Dimensions and Parameters
Setting Up Custom Dimensions
- Go to Admin > Custom Definitions in GA4
- Create custom dimensions:
page_type- Type of Carrd page (landing, portfolio, link-in-bio)visitor_type- New vs returningcampaign_source- UTM source if present
Sending Custom Dimensions
<script>
// Get UTM parameters from URL
var urlParams = new URLSearchParams(window.location.search);
var utmSource = urlParams.get('utm_source') || 'direct';
var utmMedium = urlParams.get('utm_medium') || 'none';
var utmCampaign = urlParams.get('utm_campaign') || 'none';
// Send with page view
gtag('event', 'page_view', {
'page_type': 'landing_page',
'campaign_source': utmSource,
'campaign_medium': utmMedium,
'campaign_name': utmCampaign
});
</script>
Event Naming Best Practices
Use GA4 Recommended Events
When possible, use GA4's recommended event names:
generate_lead- Form submissionsign_up- Newsletter or account signuplogin- User login (if applicable)search- Site searchselect_content- Content selectionshare- Social sharingview_item- Product or portfolio item view
Custom Event Naming
For Carrd-specific events:
- Use lowercase with underscores:
section_viewnotsectionView - Be descriptive:
portfolio_item_clicknotclick1 - Use consistent prefixes:
form_,link_,scroll_ - Keep under 40 characters
Testing Events
Use GA4 DebugView
- Enable debug mode in your GA4 config:
<script>
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
</script>
- Go to Admin > DebugView in GA4
- Publish your Carrd site
- Visit the site and trigger events
- See events in real-time with parameters
Browser Console Testing
Test events directly in console:
// Test an event
gtag('event', 'test_event', {
'test_parameter': 'test_value'
});
// Verify gtag exists
console.log(typeof gtag); // Should be "function"
// Check dataLayer
console.log(window.dataLayer);
Common Issues
Events Not Firing
Problem: Custom events don't appear in GA4.
Solutions:
- Verify GA4 is loaded: Check
typeof gtagin console (should be "function") - Test on published site: Events don't fire in Carrd preview mode
- Check embed order: Event tracking embeds must come AFTER GA4 installation embed
- Wait for processing: Events can take a few minutes to appear
- Use DebugView: Enable debug mode for immediate feedback
- Check JavaScript errors: Open console (F12) and look for errors
Events Fire Multiple Times
Problem: Same event fires multiple times per action.
Solutions:
- Check for duplicate embeds: Remove duplicate tracking codes
- Use event flags:
var eventFired = false;
button.addEventListener('click', function() {
if (!eventFired) {
eventFired = true;
gtag('event', 'button_click', {...});
}
});
- Use
onceoption:
element.addEventListener('click', handler, { once: true });
Form Tracking Not Working
Problem: Form submissions don't track.
Solutions:
- Use sendBeacon: Add
transport_type: 'beacon'to event - Place embed after form: Embed must be after form in page layout
- Check form redirect: Carrd forms redirect quickly
- Add small delay: Test with a brief setTimeout
- Verify form selector: Ensure
querySelector('form')finds your form
Complete Tracking Setup
Here's a complete event tracking setup for a typical Carrd site:
<script>
document.addEventListener('DOMContentLoaded', function() {
// 1. Form Tracking
var forms = document.querySelectorAll('form');
forms.forEach(function(form) {
form.addEventListener('submit', function() {
gtag('event', 'generate_lead', {
'form_id': form.getAttribute('id') || 'contact_form',
'transport_type': 'beacon'
});
});
});
// 2. Button Tracking
var buttons = document.querySelectorAll('[data-event]');
buttons.forEach(function(button) {
button.addEventListener('click', function() {
gtag('event', this.getAttribute('data-event'), {
'button_text': this.textContent.trim()
});
});
});
// 3. Outbound Links
var links = document.querySelectorAll('a');
links.forEach(function(link) {
link.addEventListener('click', function() {
var href = this.getAttribute('href') || '';
if (href.startsWith('http') && !href.includes(window.location.hostname)) {
gtag('event', 'click', {
'event_category': 'outbound',
'event_label': href,
'transport_type': 'beacon'
});
}
});
});
// 4. Scroll Depth
var scrollMarks = [25, 50, 75, 100];
var tracked = [];
var checkScroll = function() {
var percent = Math.round((window.pageYOffset / (document.documentElement.scrollHeight - window.innerHeight)) * 100);
scrollMarks.forEach(function(mark) {
if (percent >= mark && tracked.indexOf(mark) === -1) {
tracked.push(mark);
gtag('event', 'scroll', { 'percent_scrolled': mark });
}
});
};
window.addEventListener('scroll', function() {
setTimeout(checkScroll, 100);
});
});
</script>
Add this single embed to your Carrd site for comprehensive tracking.
Next Steps
- Install Google Tag Manager for easier event management
- Troubleshoot Events Not Firing if tracking isn't working
- Optimize Performance to maintain fast load times
- Set Up Meta Pixel for Facebook advertising