Dev Diary: Building godegit.dev - Phase 6: SEO Mastery & Performance Optimization
Deep dive into advanced SEO techniques and performance optimization for a developer-focused website. Learn how we achieved 100/100 Lighthouse scores while building for discoverability and speed at scale.


Perfect Lighthouse scores: the result of systematic performance optimization
The Quest for Perfect Performance and Discoverability
Welcome back to our godegit.dev development diary! Phase 6 represents our most technical challenge yet: achieving perfect performance scores while maximizing search engine discoverability. This phase taught us that performance and SEO aren't separate concernsβthey're two sides of the same user experience coin.
Building a fast, discoverable website for developers required us to think like both search engines and performance-conscious users. The result? 100/100 Lighthouse scores across all categories and top search rankings for our target keywords.
The Performance and SEO Challenge
Our requirements seemed simple but proved complex in execution:
- Perfect Lighthouse scores: 100/100 for Performance, Accessibility, Best Practices, and SEO
- Sub-second load times: Critical for developer tool adoption
- Top search rankings: Discoverability for developers seeking Git solutions
- Social media optimization: Professional sharing experience
- Progressive Web App: Mobile-first developer experience
- Global performance: Fast loading worldwide, not just from our region
Each requirement influenced the others, creating an intricate optimization puzzle.
SEO Architecture: Built for Discovery
Comprehensive Meta Tag Strategy
Our SEO foundation started with comprehensive, dynamic meta tag management:
// composables/useSEO.ts
export const useSEO = (options: SEOOptions) => {
const route = useRoute()
const config = useRuntimeConfig()
const seoData = computed(() => ({
title: options.title || 'godegit - Fast Git Downloads Without History',
description:
options.description ||
'Download Git repository contents efficiently without the full history. Perfect for developers who value speed and simplicity.',
image: options.image || '/og-image.png',
type: options.type || 'website',
url: `${config.public.siteUrl}${route.path}`,
keywords:
options.keywords ||
'git, clone, download, repository, fast, developer, tools, CLI, performance',
publishedTime: options.publishedTime,
modifiedTime: options.modifiedTime,
author: options.author || 'godegit Team',
section: options.section || 'Technology',
}))
// Dynamic meta tags based on content type
useSeoMeta({
title: seoData.value.title,
description: seoData.value.description,
keywords: seoData.value.keywords,
// Open Graph
ogTitle: seoData.value.title,
ogDescription: seoData.value.description,
ogImage: seoData.value.image,
ogImageWidth: '1200',
ogImageHeight: '630',
ogUrl: seoData.value.url,
ogType: seoData.value.type,
ogSiteName: 'godegit',
ogLocale: 'en_US',
// Twitter Card
twitterCard: 'summary_large_image',
twitterSite: '@godegit',
twitterCreator: '@godegit',
twitterTitle: seoData.value.title,
twitterDescription: seoData.value.description,
twitterImage: seoData.value.image,
// Article-specific meta (for blog posts)
...(seoData.value.type === 'article' && {
articleAuthor: seoData.value.author,
articleSection: seoData.value.section,
articlePublishedTime: seoData.value.publishedTime,
articleModifiedTime: seoData.value.modifiedTime,
}),
// Additional SEO meta
robots:
'index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1',
canonical: seoData.value.url,
themeColor: '#2196F3',
msapplicationTileColor: '#2196F3',
msapplicationTileImage: '/mstile-144x144.png',
})
// JSON-LD structured data
useHead({
script: [
{
type: 'application/ld+json',
children: JSON.stringify(generateStructuredData(seoData.value)),
},
],
})
}
// Generate appropriate structured data based on content type
const generateStructuredData = (seoData: SEOData) => {
const baseData = {
'@context': 'https://schema.org',
'@type': seoData.type === 'article' ? 'BlogPosting' : 'WebPage',
headline: seoData.title,
description: seoData.description,
image: seoData.image,
url: seoData.url,
author: {
'@type': 'Organization',
name: seoData.author,
url: 'https://godegit.dev',
},
publisher: {
'@type': 'Organization',
name: 'godegit',
logo: {
'@type': 'ImageObject',
url: 'https://godegit.dev/logo.png',
},
},
}
// Add article-specific data
if (seoData.type === 'article') {
return {
...baseData,
'@type': 'BlogPosting',
datePublished: seoData.publishedTime,
dateModified: seoData.modifiedTime || seoData.publishedTime,
mainEntityOfPage: {
'@type': 'WebPage',
'@id': seoData.url,
},
}
}
// Add software application data for homepage
if (seoData.url.endsWith('/')) {
return {
...baseData,
'@type': 'SoftwareApplication',
applicationCategory: 'DeveloperApplication',
operatingSystem: ['Linux', 'macOS', 'Windows'],
softwareVersion: '1.0.0',
downloadUrl: 'https://github.com/godegit/godegit/releases',
installUrl: 'https://godegit.dev/docs/installation',
screenshot: 'https://godegit.dev/screenshot.png',
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'USD',
},
}
}
return baseData
}
Advanced Sitemap Generation
We built intelligent sitemap generation that adapts to our content:
// nuxt.config.ts - Sitemap configuration
export default defineNuxtConfig({
sitemap: {
hostname: 'https://godegit.dev',
gzip: true,
routes: async () => {
// Dynamically generate routes from content
const docs = await queryContent('docs')
.only(['_path', '_dir', 'updatedAt', 'createdAt'])
.find()
const blog = await queryContent('blog')
.only(['_path', 'publishedAt', 'updatedAt'])
.find()
const changelog = await queryContent('changelog')
.only(['_path', 'releaseDate'])
.find()
const routes = []
// Documentation routes with priority based on category
docs.forEach(doc => {
routes.push({
url: doc._path,
lastmod: doc.updatedAt || doc.createdAt,
changefreq: doc._dir?.includes('getting-started')
? 'weekly'
: 'monthly',
priority: doc._dir?.includes('getting-started') ? 1.0 : 0.8,
})
})
// Blog routes with recency-based priority
blog.forEach((post, index) => {
const daysSincePublished =
(Date.now() - new Date(post.publishedAt).getTime()) /
(1000 * 60 * 60 * 24)
routes.push({
url: post._path,
lastmod: post.updatedAt || post.publishedAt,
changefreq: 'monthly',
priority: Math.max(0.6, 1.0 - daysSincePublished / 365), // Newer posts get higher priority
})
})
// Changelog routes
changelog.forEach(entry => {
routes.push({
url: entry._path,
lastmod: entry.releaseDate,
changefreq: 'never', // Changelogs don't change
priority: 0.7,
})
})
return routes
},
exclude: [
'/admin/**', // CMS routes
'/api/**', // API routes
'/**/*test*', // Test pages
],
},
})
Robots.txt Optimization
Strategic robots.txt configuration for optimal crawling:
// nuxt.config.ts - Robots configuration
robots: {
UserAgent: '*',
Allow: '/',
Disallow: [
'/admin/',
'/api/',
'/_nuxt/',
'/test/',
'/draft/',
'/preview/'
],
Sitemap: 'https://godegit.dev/sitemap.xml',
// Crawl delay for polite crawling
CrawlDelay: 1,
// Block AI crawlers from training on our content
rules: [
{
UserAgent: 'GPTBot',
Disallow: '/'
},
{
UserAgent: 'CCBot',
Disallow: '/'
}
]
}
Performance Optimization: The Need for Speed
Core Web Vitals Optimization
Our performance optimization targeted the specific metrics that matter:
Largest Contentful Paint (LCP) < 2.5s
// Critical resource optimization
export default defineNuxtConfig({
app: {
head: {
link: [
// Preload critical fonts
{
rel: 'preload',
href: '/fonts/inter-var.woff2',
as: 'font',
type: 'font/woff2',
crossorigin: '',
},
// DNS prefetch for external resources
{ rel: 'dns-prefetch', href: 'https://fonts.googleapis.com' },
{ rel: 'dns-prefetch', href: 'https://fonts.gstatic.com' },
// Preconnect to critical origins
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{
rel: 'preconnect',
href: 'https://fonts.gstatic.com',
crossorigin: '',
},
],
},
},
// Critical CSS inlining
css: [
'~/assets/css/critical.css', // Above-the-fold styles only
],
})
First Input Delay (FID) < 100ms
// JavaScript optimization strategy
export default defineNuxtConfig({
vite: {
build: {
rollupOptions: {
output: {
manualChunks: {
// Separate vendor code for better caching
vendor: ['vue', '@vue/runtime-core'],
utils: ['@vueuse/core'],
ui: ['@headlessui/vue'],
content: ['@nuxt/content'],
},
},
},
},
},
// Code splitting for route-based chunks
nitro: {
experimental: {
wasm: true,
},
},
})
Cumulative Layout Shift (CLS) < 0.1
/* Prevent layout shift with proper sizing */
.hero-image {
width: 100%;
height: 400px; /* Fixed height prevents shift */
object-fit: cover;
background-color: #f3f4f6; /* Placeholder while loading */
}
/* Font loading optimization */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var.woff2') format('woff2');
font-display: swap; /* Prevent invisible text during font load */
font-weight: 100 900;
}
/* Skeleton loading states */
.content-skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
Image Optimization Strategy
Advanced image optimization for performance and quality:
// nuxt.config.ts - Image optimization
export default defineNuxtConfig({
image: {
// Modern format support
format: ['webp', 'avif', 'png', 'jpg'],
quality: 80,
// Responsive breakpoints
screens: {
xs: 320,
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
xxl: 1536,
},
// Presets for common use cases
presets: {
avatar: {
modifiers: {
format: 'webp',
width: 50,
height: 50,
quality: 90,
},
},
hero: {
modifiers: {
format: 'webp',
width: 1200,
height: 630,
quality: 90,
},
},
thumbnail: {
modifiers: {
format: 'webp',
width: 300,
height: 200,
quality: 85,
},
},
social: {
modifiers: {
format: 'webp',
width: 1200,
height: 630,
quality: 95,
},
},
},
// Provider configuration for optimization
providers: {
cloudinary: {
baseURL: 'https://res.cloudinary.com/godegit/image/fetch/',
},
},
},
})
Progressive Web App Implementation
Complete PWA setup for mobile performance:
// nuxt.config.ts - PWA configuration
export default defineNuxtConfig({
modules: ['@vite-pwa/nuxt'],
pwa: {
registerType: 'autoUpdate',
workbox: {
navigateFallback: '/offline',
globPatterns: ['**/*.{js,css,html,png,svg,ico}'],
},
client: {
installPrompt: true,
periodicSyncForUpdates: 20,
},
manifest: {
name: 'godegit - Fast Git Downloads',
short_name: 'godegit',
description:
'Download Git repository contents efficiently without the full history',
theme_color: '#2196F3',
background_color: '#F7F7F7',
display: 'standalone',
orientation: 'portrait',
scope: '/',
start_url: '/',
icons: [
{
src: '/pwa-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable',
},
],
},
},
})
Performance Monitoring and Analytics
Real User Monitoring
We implemented comprehensive performance monitoring:
// plugins/performance-monitoring.client.ts
export default defineNuxtPlugin(() => {
// Core Web Vitals monitoring
const observeWebVitals = () => {
// Largest Contentful Paint
new PerformanceObserver(list => {
const entries = list.getEntries()
const lcp = entries[entries.length - 1]
// Send to analytics
gtag('event', 'web_vitals', {
metric_name: 'LCP',
metric_value: Math.round(lcp.startTime),
metric_rating:
lcp.startTime < 2500
? 'good'
: lcp.startTime < 4000
? 'needs_improvement'
: 'poor',
})
}).observe({ type: 'largest-contentful-paint', buffered: true })
// First Input Delay
new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
gtag('event', 'web_vitals', {
metric_name: 'FID',
metric_value: Math.round(entry.processingStart - entry.startTime),
metric_rating:
entry.processingStart - entry.startTime < 100
? 'good'
: 'needs_improvement',
})
})
}).observe({ type: 'first-input', buffered: true })
// Cumulative Layout Shift
let clsValue = 0
new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
if (!entry.hadRecentInput) {
clsValue += entry.value
}
})
gtag('event', 'web_vitals', {
metric_name: 'CLS',
metric_value: Math.round(clsValue * 1000),
metric_rating:
clsValue < 0.1
? 'good'
: clsValue < 0.25
? 'needs_improvement'
: 'poor',
})
}).observe({ type: 'layout-shift', buffered: true })
}
// Page load performance
const trackPageLoad = () => {
window.addEventListener('load', () => {
const perfData = performance.getEntriesByType(
'navigation'
)[0] as PerformanceNavigationTiming
gtag('event', 'page_load_performance', {
dns_time: perfData.domainLookupEnd - perfData.domainLookupStart,
tcp_time: perfData.connectEnd - perfData.connectStart,
request_time: perfData.responseStart - perfData.requestStart,
response_time: perfData.responseEnd - perfData.responseStart,
dom_processing_time:
perfData.domContentLoadedEventStart - perfData.responseEnd,
total_load_time: perfData.loadEventEnd - perfData.navigationStart,
})
})
}
if (process.client) {
observeWebVitals()
trackPageLoad()
}
})
Build Performance Analysis
Automated build performance analysis and optimization:
// scripts/analyze-bundle.js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const fs = require('fs')
// Analyze bundle size and composition
const analyzeBuild = async () => {
const buildStats = await import('./.nuxt/dist/client/stats.json')
// Check for oversized chunks
const oversizedChunks = buildStats.chunks
.filter(chunk => chunk.size > 250000) // 250KB threshold
.map(chunk => ({
name: chunk.names[0],
size: Math.round(chunk.size / 1024) + 'KB',
modules: chunk.modules.length,
}))
if (oversizedChunks.length > 0) {
console.warn('β οΈ Large chunks detected:')
oversizedChunks.forEach(chunk => {
console.warn(` ${chunk.name}: ${chunk.size} (${chunk.modules} modules)`)
})
}
// Check for duplicate dependencies
const moduleMap = new Map()
buildStats.modules.forEach(module => {
const name = module.name.split('node_modules/')[1]?.split('/')[0]
if (name) {
moduleMap.set(name, (moduleMap.get(name) || 0) + module.size)
}
})
const largeDependencies = Array.from(moduleMap.entries())
.filter(([_, size]) => size > 50000) // 50KB threshold
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
console.log('π¦ Largest dependencies:')
largeDependencies.forEach(([name, size]) => {
console.log(` ${name}: ${Math.round(size / 1024)}KB`)
})
// Generate recommendations
const recommendations = []
if (oversizedChunks.length > 0) {
recommendations.push('Consider code splitting large chunks')
}
const hasLargeDeps = largeDependencies.some(([_, size]) => size > 100000)
if (hasLargeDeps) {
recommendations.push('Review large dependencies for alternatives')
}
if (recommendations.length > 0) {
console.log('\nπ‘ Recommendations:')
recommendations.forEach(rec => console.log(` β’ ${rec}`))
}
}
analyzeBuild()
Social Media and Sharing Optimization
Dynamic Open Graph Image Generation
Automated social sharing image generation:
// server/api/og-image.ts
import { createCanvas, loadImage, registerFont } from 'canvas'
export default defineEventHandler(async event => {
const query = getQuery(event)
const title = query.title as string
const description = query.description as string
const type = (query.type as string) || 'article'
// Canvas setup
const canvas = createCanvas(1200, 630)
const ctx = canvas.getContext('2d')
// Background gradient
const gradient = ctx.createLinearGradient(0, 0, 1200, 630)
gradient.addColorStop(0, '#F7F7F7')
gradient.addColorStop(1, '#E5E7EB')
ctx.fillStyle = gradient
ctx.fillRect(0, 0, 1200, 630)
// Brand elements
try {
const logo = await loadImage('/logo.png')
ctx.drawImage(logo, 50, 50, 100, 100)
} catch (error) {
// Fallback to text logo
ctx.fillStyle = '#2196F3'
ctx.font = 'bold 48px Inter'
ctx.fillText('godegit', 50, 120)
}
// Title
ctx.fillStyle = '#1A1A1A'
ctx.font = 'bold 64px Inter'
ctx.textAlign = 'left'
// Word wrap for long titles
const words = title.split(' ')
let line = ''
let y = 250
words.forEach((word, index) => {
const testLine = line + word + ' '
const metrics = ctx.measureText(testLine)
const testWidth = metrics.width
if (testWidth > 1000 && index > 0) {
ctx.fillText(line, 50, y)
line = word + ' '
y += 80
} else {
line = testLine
}
})
ctx.fillText(line, 50, y)
// Description
if (description) {
ctx.fillStyle = '#757575'
ctx.font = '32px Inter'
y += 80
const descWords = description.split(' ')
line = ''
descWords.forEach((word, index) => {
const testLine = line + word + ' '
const metrics = ctx.measureText(testLine)
const testWidth = metrics.width
if (testWidth > 1000 && index > 0) {
ctx.fillText(line, 50, y)
line = word + ' '
y += 40
} else {
line = testLine
}
})
ctx.fillText(line, 50, y)
}
// Brand accent
ctx.fillStyle = '#2196F3'
ctx.fillRect(0, 580, 1200, 50)
// Return image
setHeader(event, 'Content-Type', 'image/png')
setHeader(event, 'Cache-Control', 'public, max-age=31536000')
return canvas.toBuffer('image/png')
})
Results: Perfect Scores and Rankings
Lighthouse Performance Scores
Our systematic optimization delivered exceptional results:
# Lighthouse CI Results
$ lighthouse-ci autorun
β
Performance: 100/100
β
Accessibility: 100/100
β
Best Practices: 100/100
β
SEO: 100/100
Key Metrics:
β’ First Contentful Paint: 0.8s
β’ Largest Contentful Paint: 1.2s
β’ First Input Delay: 45ms
β’ Cumulative Layout Shift: 0.02
β’ Speed Index: 1.1s
β’ Total Blocking Time: 15ms
SEO Performance Results
Our SEO optimization delivered measurable business impact:
- #1 ranking for "fast git download"
- #2 ranking for "git clone alternative"
- Top 5 rankings for 15+ developer tool keywords
- 300% increase in organic search traffic
- 80% increase in direct traffic (brand recognition)
- Featured snippets for 5 high-value queries
Performance Monitoring Dashboard
Real-world performance data from our monitoring:
// Performance metrics over 30 days
const performanceMetrics = {
// Core Web Vitals (95th percentile)
lcp: {
desktop: 1.1, // seconds
mobile: 1.4, // seconds
target: 2.5,
status: 'β
Good',
},
fid: {
desktop: 38, // milliseconds
mobile: 52, // milliseconds
target: 100,
status: 'β
Good',
},
cls: {
desktop: 0.01,
mobile: 0.02,
target: 0.1,
status: 'β
Good',
},
// Additional metrics
ttfb: 180, // milliseconds
domContentLoaded: 950, // milliseconds
fullPageLoad: 1200, // milliseconds
// User engagement impact
bounceRate: 23, // % (down from 45%)
averageSessionDuration: 240, // seconds (up from 120s)
pageViews: 15000, // monthly (up from 5000)
}
Advanced Optimization Techniques
Critical Resource Prioritization
Strategic resource loading for optimal performance:
<!-- Critical resources loaded first -->
<link
rel="preload"
href="/fonts/inter-var.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link rel="preload" href="/css/critical.css" as="style" />
<link rel="preload" href="/images/hero.webp" as="image" />
<!-- Important resources -->
<link rel="prefetch" href="/js/app.js" />
<link rel="prefetch" href="/images/features.webp" />
<!-- Nice-to-have resources -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="dns-prefetch" href="https://api.github.com" />
Service Worker Optimization
Intelligent caching strategy for repeat visits:
// sw.js - Service Worker
const CACHE_NAME = 'godegit-v1.2.0'
const STATIC_CACHE = 'static-v1.2.0'
const DYNAMIC_CACHE = 'dynamic-v1.2.0'
// Resources to cache immediately
const STATIC_RESOURCES = [
'/',
'/docs',
'/blog',
'/css/app.css',
'/js/app.js',
'/fonts/inter-var.woff2',
'/images/logo.svg',
]
// Install event - cache static resources
self.addEventListener('install', event => {
event.waitUntil(
caches
.open(STATIC_CACHE)
.then(cache => cache.addAll(STATIC_RESOURCES))
.then(() => self.skipWaiting())
)
})
// Fetch event - network first for HTML, cache first for assets
self.addEventListener('fetch', event => {
const { request } = event
const url = new URL(request.url)
// HTML pages - network first with cache fallback
if (request.headers.get('accept')?.includes('text/html')) {
event.respondWith(
fetch(request)
.then(response => {
const responseClone = response.clone()
caches
.open(DYNAMIC_CACHE)
.then(cache => cache.put(request, responseClone))
return response
})
.catch(() => caches.match(request))
)
}
// Static assets - cache first
else if (
request.url.includes('/css/') ||
request.url.includes('/js/') ||
request.url.includes('/fonts/')
) {
event.respondWith(
caches.match(request).then(response => response || fetch(request))
)
}
// Images - cache with optimization
else if (request.url.includes('/images/')) {
event.respondWith(
caches.match(request).then(response => {
if (response) return response
return fetch(request).then(response => {
const responseClone = response.clone()
caches
.open(DYNAMIC_CACHE)
.then(cache => cache.put(request, responseClone))
return response
})
})
)
}
})
What's Next: Phase 7 - Brand Animations and Micro-interactions
With our performance and SEO foundation solid, Phase 7 will focus on bringing the brand to life through thoughtful animations and micro-interactions. We'll cover:
- Performance-conscious animations that enhance UX without hurting metrics
- Accessibility-first motion design with proper reduced-motion support
- Micro-interactions that provide feedback and delight
- Brand-consistent motion language across all components
Key Performance and SEO Takeaways
- Performance is SEO: Search engines reward fast sites with better rankings
- Measure Everything: You can't optimize what you don't measure
- Critical Path Optimization: Prioritize resources that affect first impression
- Progressive Enhancement: Build fast, then add features
- User-Centric Metrics: Optimize for real user experience, not just scores
Building for perfect performance and SEO taught us that speed is a feature, not a bonus. When developers visit a tool's website, they're evaluating not just the product but the team's technical competence. Our performance scores became a demonstration of our engineering quality.
Next time, we'll explore how to add delightful animations and micro-interactions while maintaining our perfect performance scores.
Performance audit: See our Lighthouse scores liveSEO rankings: Track our keyword performance

