Jekyll Website Optimization for GitHub Pages - Part 2
· 11 min readIn Part 1 of this series, we covered the foundational optimizations for Jekyll sites on GitHub Pages. Part 2 dives into advanced analytics, performance monitoring, and the lessons learned from running a technical blog with 100+ posts.
Advanced Analytics and Monitoring
Google Analytics 4 Integration
Beyond basic page tracking, GA4 provides valuable insights for technical blogs:
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-F90DVB199P"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-F90DVB199P', {
// Enhanced measurement for technical content
enhanced_measurement_settings: {
scrolls_enabled: true,
outbound_clicks_enabled: true,
site_search_enabled: true,
video_engagement_enabled: true,
file_downloads_enabled: true
}
});
</script>
Custom Event Tracking
Track specific interactions valuable for technical content:
// Track code copy button usage
document.addEventListener('click', function(e) {
if (e.target.classList.contains('copy-button')) {
gtag('event', 'code_copy', {
event_category: 'engagement',
event_label: 'code_snippet_copied'
});
}
});
// Track external link clicks
document.addEventListener('click', function(e) {
if (e.target.tagName === 'A' && e.target.hostname !== window.location.hostname) {
gtag('event', 'click', {
event_category: 'outbound',
event_label: e.target.href,
transport_type: 'beacon'
});
}
});
Performance Monitoring
Key metrics to track for technical blogs:
// Core Web Vitals tracking
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
gtag('event', 'web_vitals', {
event_category: 'performance',
event_label: 'LCP',
value: Math.round(entry.startTime)
});
}
}
}).observe({entryTypes: ['largest-contentful-paint']});
// Track page load performance
window.addEventListener('load', function() {
const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
gtag('event', 'page_load_time', {
event_category: 'performance',
value: Math.round(loadTime)
});
});
Remaining Optimization Priorities
Based on analytics data and user feedback, here are the high-impact optimizations still on my roadmap:
High Priority Enhancements
1. Dark/Light Theme Toggle
User preference support with system detection:
// Theme toggle implementation
const themeToggle = document.getElementById('theme-toggle');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
gtag('event', 'theme_change', {
event_category: 'user_preference',
event_label: theme
});
}
// Initialize theme
const savedTheme = localStorage.getItem('theme') ||
(prefersDark.matches ? 'dark' : 'light');
setTheme(savedTheme);
2. Site Search Implementation
Google Custom Search integration for 100+ posts:
<div class="search-container">
<script async src="https://cse.google.com/cse.js?cx=YOUR_SEARCH_ENGINE_ID"></script>
<div class="gcse-search"></div>
</div>
3. Reading Progress Indicator
Visual feedback for long technical articles:
window.addEventListener('scroll', function() {
const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
const scrolled = (winScroll / height) * 100;
document.getElementById('progress-bar').style.width = scrolled + '%';
});
Medium Priority Features
1. Breadcrumb Navigation
Especially important for categorized technical content:
<nav class="breadcrumb">
<a href="/">Home</a>
{% if page.categories %}
{% for category in page.categories %}
<span class="separator">›</span>
<a href="/categories/{{ category | slugify }}/">{{ category | capitalize }}</a>
{% endfor %}
{% endif %}
<span class="separator">›</span>
<span class="current">{{ page.title }}</span>
</nav>
2. Social Share Buttons
Increase content distribution:
<div class="share-buttons">
<a href="https://twitter.com/intent/tweet?url={{ site.url }}{{ page.url }}&text={{ page.title }}"
target="_blank" rel="noopener">
Share on Twitter
</a>
<a href="https://www.linkedin.com/sharing/share-offsite/?url={{ site.url }}{{ page.url }}"
target="_blank" rel="noopener">
Share on LinkedIn
</a>
</div>
3. Cookie Consent Banner
GDPR compliance for international readers:
function showCookieConsent() {
const consent = localStorage.getItem('cookie-consent');
if (!consent) {
document.getElementById('cookie-banner').style.display = 'block';
}
}
function acceptCookies() {
localStorage.setItem('cookie-consent', 'accepted');
document.getElementById('cookie-banner').style.display = 'none';
// Initialize analytics after consent
initializeAnalytics();
}
Technical Improvements
1. Custom Error Pages
Enhanced 404/500 pages with search functionality:
<!-- 404.html -->
---
layout: default
permalink: /404.html
---
<div class="error-page">
<h1>Page Not Found</h1>
<p>The page you're looking for doesn't exist. Try searching:</p>
<div class="gcse-search"></div>
<h3>Popular Articles</h3>
{% for post in site.posts limit:5 %}
<article>
<a href="{{ post.url }}">{{ post.title }}</a>
<p>{{ post.excerpt | strip_html | truncate: 100 }}</p>
</article>
{% endfor %}
</div>
2. Lazy Loading Implementation
Images load on demand to improve initial page load:
// Intersection Observer for lazy loading
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
3. Responsive Image Sets
Multiple image sizes for different devices:
<picture>
<source media="(max-width: 480px)" srcset="/assets/images/image-small.webp">
<source media="(max-width: 768px)" srcset="/assets/images/image-medium.webp">
<source media="(min-width: 769px)" srcset="/assets/images/image-large.webp">
<img src="/assets/images/image-large.jpg" alt="Description" loading="lazy">
</picture>
Performance Measurement Results
Before and After Metrics
Implementing the optimizations from both parts has yielded measurable improvements:
Page Load Performance
- Initial load time: Reduced from 3.2s to 1.6s (50% improvement)
- Time to Interactive: Improved from 4.1s to 2.1s (49% improvement)
- First Contentful Paint: Reduced from 1.8s to 0.9s (50% improvement)
Core Web Vitals
- Largest Contentful Paint: Improved from 2.8s to 1.4s
- First Input Delay: Reduced from 120ms to 45ms
- Cumulative Layout Shift: Decreased from 0.15 to 0.05
User Engagement
- Session duration: Increased by 30% (better navigation and related posts)
- Pages per session: Improved by 25% (better content organization)
- Bounce rate: Reduced by 35% (faster loading, better UX)
Mobile Performance
- Mobile PageSpeed score: Improved from 65 to 87
- Mobile usability: No issues detected
- Mobile traffic: Increased by 40% (better mobile experience)
Analytics Insights
Key findings from Google Analytics after optimizations:
Content Performance
- Technical tutorials have 45% longer session duration
- Code-heavy posts show 60% higher engagement with copy buttons
- Series articles (like this two-part post) have 25% better retention
Traffic Sources
- Organic search: 65% of traffic (improved SEO working)
- Direct traffic: 20% (returning readers, good sign)
- Social media: 10% (room for improvement)
- Referrals: 5% (technical community links)
Popular Content Categories
- Proxmox/Ceph articles: Highest engagement and sharing
- Dell Wyse 3040 series: Strong search traffic
- Jekyll/GitHub Pages: Good for developer audience
- Networking tutorials: Consistent performance
Lessons Learned from 100+ Posts
What Worked Exceptionally Well
1. Consistent Technical Focus
Focusing on homelab and infrastructure topics created a loyal readership. Technical depth matters more than broad appeal for this audience.
2. Series-Based Content
Multi-part articles (like this one) perform better than single long posts:
- Better reader retention
- Higher return visitor rates
- More social sharing
- Better SEO for related keywords
3. Real-World Examples
Posts with actual command outputs and screenshots perform 40% better than theoretical content.
4. GitHub Integration
Using GitHub for comments, hosting, and workflows creates a seamless developer experience.
What Needed Adjustment
1. Mobile Experience
Initially underestimated mobile traffic (now 35% of visitors). Mobile optimization became crucial.
2. Search Functionality
With 100+ posts, site search became essential. Readers couldn’t find older content easily.
3. Content Organization
Tags and categories needed refinement as content volume grew. Initial organization didn’t scale well.
4. Performance at Scale
Image optimization and lazy loading became critical as the site grew.
Unexpected Discoveries
1. International Audience
40% of traffic comes from outside the US, making GDPR compliance and internationalization important.
2. Long-Tail SEO Success
Specific technical problems (like “Dell Wyse 3040 Proxmox”) drive consistent traffic months after publication.
3. Community Building
Technical readers prefer GitHub Discussions over traditional comments. The developer-focused audience appreciates the familiar interface.
4. Content Longevity
Technical tutorials have a much longer useful life than expected. Posts from 2023 still drive significant traffic.
Future Enhancement Roadmap
Short-term (Next 3 months)
- Implement dark/light theme toggle
- Add site search functionality
- Create custom 404 page with search
- Implement reading progress indicators
Medium-term (3-6 months)
- Add breadcrumb navigation
- Implement lazy loading for images
- Create responsive image sets
- Add social share buttons
Long-term (6+ months)
- Multi-language support (considering Spanish)
- Newsletter signup integration
- Advanced search with filtering
- Mobile app manifest (PWA)
Infrastructure Improvements
- CDN implementation for assets
- Advanced caching strategies
- Security headers implementation
- Automated backup system
Conclusion
Running a technical blog on Jekyll and GitHub Pages has been incredibly rewarding. The platform’s flexibility allows for continuous optimization while maintaining simplicity.
Key Success Factors
- Focus on reader value: Every optimization should improve the reader experience
- Measure everything: Use analytics to guide optimization priorities
- Iterate continuously: Small, consistent improvements compound over time
- Embrace the platform: Work with Jekyll’s strengths rather than against them
Final Recommendations
For anyone running a technical blog on Jekyll:
Essential Optimizations
- Code copy buttons (highest reader satisfaction)
- Proper SEO meta tags (crucial for discoverability)
- GitHub Discussions comments (better than traditional systems)
- Automated deployment (reduces friction)
Performance Priorities
- Image optimization (biggest impact on load times)
- Lazy loading (especially for image-heavy technical content)
- Minimal JavaScript (keep it fast)
- Mobile optimization (growing audience segment)
Content Strategy
- Focus on niche technical topics
- Use real-world examples and outputs
- Create series for complex topics
- Maintain consistent publishing schedule
The combination of Jekyll’s flexibility, GitHub’s reliability, and thoughtful optimization creates an excellent platform for technical content. The key is continuous improvement based on reader feedback and analytics data.
References
- Google Analytics 4 Documentation
- Core Web Vitals Guide
- Jekyll Performance Tips
- GitHub Pages Optimization
- Web Performance Best Practices
Related Posts
- Jekyll Website Optimization Part 1 — First round of improvements
- Jekyll SEO Health Checks — Automated SEO and performance monitoring
- Implementing GDPR Compliance for Jekyll — Privacy compliance implementation
- Adding Google Custom Search to Jekyll — Site search feature
- How the Sausage Is Made — Complete feature reference for this blog