Technical SEO for Developers
Introduction
Technical SEO ensures search engines can crawl, index, and understand your website. This guide covers meta tags, structured data with Schema.org, Core Web Vitals, crawlability, sitemaps, robots.txt, and performance optimization for better search rankings.
1. Essential Meta Tags
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Character encoding -->
<meta charset="UTF-8">
<!-- Viewport for mobile -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Title (50-60 characters) -->
<title>Best JavaScript Frameworks 2025 | Developer Guide</title>
<!-- Description (150-160 characters) -->
<meta name="description" content="Complete guide to the best JavaScript frameworks in 2025 including React, Vue, Angular, and Svelte with comparisons and use cases.">
<!-- Canonical URL (prevent duplicate content) -->
<link rel="canonical" href="https://example.com/javascript-frameworks">
<!-- Open Graph (Facebook, LinkedIn) -->
<meta property="og:title" content="Best JavaScript Frameworks 2025">
<meta property="og:description" content="Complete guide to choosing the right JavaScript framework for your project.">
<meta property="og:image" content="https://example.com/images/js-frameworks.jpg">
<meta property="og:url" content="https://example.com/javascript-frameworks">
<meta property="og:type" content="article">
<meta property="og:site_name" content="Developer Guide">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Best JavaScript Frameworks 2025">
<meta name="twitter:description" content="Complete guide to choosing the right JavaScript framework.">
<meta name="twitter:image" content="https://example.com/images/js-frameworks.jpg">
<meta name="twitter:site" content="@yourusername">
<!-- Robots directives -->
<meta name="robots" content="index, follow">
<!-- Or for no-index pages: -->
<!-- <meta name="robots" content="noindex, nofollow"> -->
<!-- Author -->
<meta name="author" content="Your Name">
<!-- Keywords (less important now, but still used) -->
<meta name="keywords" content="javascript, frameworks, react, vue, angular">
</head>
<body>
</body>
</html>
// React component for dynamic meta tags
import { Helmet } from 'react-helmet-async';
function BlogPost({ post }) {
return (
<>
<Helmet>
<title>{post.title} | My Blog</title>
<meta name="description" content={post.excerpt} />
<link rel="canonical" href={`https://myblog.com/posts/${post.slug}`} />
<meta property="og:title" content={post.title} />
<meta property="og:description" content={post.excerpt} />
<meta property="og:image" content={post.coverImage} />
<meta property="og:type" content="article" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={post.title} />
<meta name="twitter:image" content={post.coverImage} />
</Helmet>
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
</>
);
}
// Next.js meta tags
import Head from 'next/head';
export default function Page() {
return (
<>
<Head>
<title>My Page Title</title>
<meta name="description" content="Page description" />
<meta property="og:title" content="My Page Title" />
<link rel="canonical" href="https://example.com/page" />
</Head>
<main>
{/* Content */}
</main>
</>
);
}
2. Structured Data (Schema.org)
<!-- JSON-LD format (recommended by Google) -->
<!-- Article schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Best JavaScript Frameworks 2025",
"description": "Complete guide to JavaScript frameworks",
"image": "https://example.com/images/js-frameworks.jpg",
"author": {
"@type": "Person",
"name": "John Doe",
"url": "https://example.com/author/john-doe"
},
"publisher": {
"@type": "Organization",
"name": "Developer Guide",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png"
}
},
"datePublished": "2025-01-15",
"dateModified": "2025-10-18"
}
</script>
<!-- Product schema (e-commerce) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Wireless Headphones",
"image": "https://example.com/images/headphones.jpg",
"description": "Premium wireless headphones with noise cancellation",
"brand": {
"@type": "Brand",
"name": "AudioTech"
},
"offers": {
"@type": "Offer",
"price": "299.99",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"url": "https://example.com/products/wireless-headphones"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.5",
"reviewCount": "127"
}
}
</script>
<!-- Local Business schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Restaurant",
"name": "The Gourmet Kitchen",
"image": "https://example.com/restaurant.jpg",
"address": {
"@type": "PostalAddress",
"streetAddress": "123 Main St",
"addressLocality": "New York",
"addressRegion": "NY",
"postalCode": "10001",
"addressCountry": "US"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 40.7128,
"longitude": -74.0060
},
"telephone": "+1-212-555-0123",
"priceRange": "$$",
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "11:00",
"closes": "22:00"
}
]
}
</script>
<!-- Breadcrumb schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://example.com"
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": "https://example.com/blog"
},
{
"@type": "ListItem",
"position": 3,
"name": "JavaScript Frameworks",
"item": "https://example.com/blog/javascript-frameworks"
}
]
}
</script>
// React component for structured data
function StructuredData({ data }) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
/>
);
}
// Usage
<StructuredData data={{
"@context": "https://schema.org",
"@type": "Article",
"headline": "My Article Title",
// ... rest of schema
}} />
3. Core Web Vitals
// Core Web Vitals are Google's performance metrics
// 1. Largest Contentful Paint (LCP) - Loading performance
// Target: < 2.5 seconds
// Optimize images
<img
src="image.jpg"
alt="Description"
loading="lazy"
width="800"
height="600"
/>
// Use modern formats
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.avif" type="image/avif">
<img src="image.jpg" alt="Description">
</picture>
// Preload critical resources
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/hero-image.jpg" as="image">
// 2. First Input Delay (FID) - Interactivity
// Target: < 100 milliseconds
// Code splitting to reduce JavaScript bundle size
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
// Defer non-critical JavaScript
<script src="analytics.js" defer></script>
<script src="chat-widget.js" async></script>
// 3. Cumulative Layout Shift (CLS) - Visual stability
// Target: < 0.1
// Always specify image dimensions
<img src="image.jpg" width="800" height="600" alt="Description">
// Reserve space for ads
.ad-container {
min-height: 250px;
}
// Use CSS aspect ratio for responsive images
.image-container {
aspect-ratio: 16 / 9;
}
// Measure Core Web Vitals
import { getCLS, getFID, getLCP } from 'web-vitals';
getCLS(console.log);
getFID(console.log);
getLCP(console.log);
// Send to analytics
function sendToAnalytics(metric) {
const body = JSON.stringify(metric);
if (navigator.sendBeacon) {
navigator.sendBeacon('/analytics', body);
} else {
fetch('/analytics', { body, method: 'POST', keepalive: true });
}
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);
// Performance optimization
// Resource hints
<link rel="dns-prefetch" href="https://cdn.example.com">
<link rel="preconnect" href="https://api.example.com">
<link rel="prefetch" href="/next-page.html">
// Critical CSS inline
<style>
/* Critical above-the-fold CSS */
body { font-family: sans-serif; }
.header { background: #000; }
</style>
// Load rest of CSS async
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
4. Sitemaps & Robots.txt
<!-- sitemap.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<lastmod>2025-10-18</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://example.com/about</loc>
<lastmod>2025-10-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://example.com/blog/javascript-frameworks</loc>
<lastmod>2025-10-15</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
</urlset>
// Generate sitemap dynamically (Node.js)
const { SitemapStream, streamToPromise } = require('sitemap');
const { createWriteStream } = require('fs');
async function generateSitemap() {
const stream = new SitemapStream({ hostname: 'https://example.com' });
const writeStream = createWriteStream('./public/sitemap.xml');
stream.pipe(writeStream);
// Add static pages
stream.write({ url: '/', changefreq: 'daily', priority: 1.0 });
stream.write({ url: '/about', changefreq: 'monthly', priority: 0.8 });
// Add dynamic pages from database
const posts = await db.posts.findAll();
posts.forEach(post => {
stream.write({
url: `/blog/${post.slug}`,
lastmod: post.updatedAt,
changefreq: 'weekly',
priority: 0.9
});
});
stream.end();
}
// robots.txt
User-agent: *
Allow: /
Disallow: /admin/
Disallow: /api/
Disallow: /private/
# Allow Google Images
User-agent: Googlebot-Image
Allow: /images/
# Block specific bots
User-agent: BadBot
Disallow: /
# Sitemap location
Sitemap: https://example.com/sitemap.xml
// Express endpoint for robots.txt
app.get('/robots.txt', (req, res) => {
res.type('text/plain');
res.send(`
User-agent: *
Allow: /
Disallow: /admin/
Sitemap: https://example.com/sitemap.xml
`.trim());
});
5. URL Structure & Internal Linking
// Good URL structure
// ✅ Good: Descriptive, hierarchical
https://example.com/blog/javascript-frameworks
https://example.com/products/electronics/laptops/macbook-pro
https://example.com/docs/api/authentication
// ❌ Bad: Unclear, non-descriptive
https://example.com/p?id=12345
https://example.com/article.php?cat=3&post=567
// URL best practices
// 1. Use hyphens, not underscores
/javascript-frameworks ✅
/javascript_frameworks ❌
// 2. Lowercase only
/JavaScript-Frameworks ❌
/javascript-frameworks ✅
// 3. Keep it short and descriptive
/best-js-frameworks-for-modern-web-development-in-2025 ❌
/javascript-frameworks-2025 ✅
// 4. Include keywords naturally
/post-12345 ❌
/react-vs-vue-comparison ✅
// Internal linking
<nav>
<a href="/blog">Blog</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
<!-- Contextual internal links in content -->
<p>
For more information, check out our
<a href="/javascript-frameworks">JavaScript frameworks guide</a>.
</p>
<!-- Breadcrumbs -->
<nav aria-label="Breadcrumb">
<ol>
<li><a href="/">Home</a></li>
<li><a href="/blog">Blog</a></li>
<li aria-current="page">JavaScript Frameworks</li>
</ol>
</nav>
// Next.js Link component (preserves prefetching)
import Link from 'next/link';
<Link href="/blog/javascript-frameworks">
JavaScript Frameworks Guide
</Link>
// Canonical URLs for duplicate content
<!-- Original page -->
<link rel="canonical" href="https://example.com/products/laptop">
<!-- Duplicate pages should point to original -->
<link rel="canonical" href="https://example.com/products/laptop">
// 301 Redirects for moved content
// Express
app.get('/old-url', (req, res) => {
res.redirect(301, '/new-url');
});
// Next.js (next.config.js)
module.exports = {
async redirects() {
return [
{
source: '/old-url',
destination: '/new-url',
permanent: true, // 301 redirect
},
];
},
};
6. Mobile-First & Responsive Design
<!-- Viewport meta tag (essential) -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Responsive images -->
<img
srcset="
image-320w.jpg 320w,
image-640w.jpg 640w,
image-1024w.jpg 1024w
"
sizes="(max-width: 320px) 280px,
(max-width: 640px) 600px,
1000px"
src="image-640w.jpg"
alt="Description"
>
// CSS media queries
/* Mobile first approach */
.container {
padding: 1rem;
}
@media (min-width: 768px) {
.container {
padding: 2rem;
}
}
@media (min-width: 1024px) {
.container {
padding: 3rem;
}
}
// Test mobile usability
// Google Mobile-Friendly Test: https://search.google.com/test/mobile-friendly
// Chrome DevTools device emulation
// Lighthouse audit
// Run in Chrome DevTools or CLI
npx lighthouse https://example.com --view
7. Page Speed Optimization
// Compress images
// Use tools like: ImageOptim, TinyPNG, Squoosh
// Next.js Image component (automatic optimization)
import Image from 'next/image';
<Image
src="/photo.jpg"
alt="Description"
width={800}
height={600}
priority // For LCP image
/>
// Lazy load images
<img src="image.jpg" loading="lazy" alt="Description">
// Minify CSS and JavaScript
// Build tools handle this automatically
// Webpack, Vite, Parcel
// Enable Gzip/Brotli compression
// Express with compression middleware
const compression = require('compression');
app.use(compression());
// Nginx configuration
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;
// HTTP/2 Server Push
<link rel="preload" href="/critical.css" as="style">
<link rel="preload" href="/critical.js" as="script">
// CDN for static assets
<script src="https://cdn.example.com/library.min.js"></script>
// Browser caching
// Express
app.use(express.static('public', {
maxAge: '1y',
etag: false
}));
// Nginx
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
8. Best Practices
✓ Technical SEO Best Practices:
- ✓ Use descriptive, keyword-rich page titles (50-60 chars)
- ✓ Write compelling meta descriptions (150-160 chars)
- ✓ Implement structured data (Schema.org JSON-LD)
- ✓ Optimize Core Web Vitals (LCP, FID, CLS)
- ✓ Create and submit XML sitemap
- ✓ Use clean, descriptive URLs with keywords
- ✓ Implement proper heading hierarchy (H1-H6)
- ✓ Add alt text to all images
- ✓ Ensure mobile-friendly responsive design
- ✓ Use HTTPS for security (ranking factor)
- ✓ Implement 301 redirects for moved content
- ✓ Fix broken links and 404 errors
- ✓ Optimize page load speed (< 3 seconds)
- ✓ Use internal linking strategically
- ✓ Create robots.txt for crawl control
Conclusion
Technical SEO is essential for search engine visibility. Implement proper meta tags, structured data, optimize Core Web Vitals, ensure mobile-friendliness, and improve page speed. Use sitemaps, robots.txt, and clean URLs. Regular audits with Google Search Console and Lighthouse help maintain and improve SEO performance.
💡 Pro Tip: Set up Google Search Console and Bing Webmaster Tools to monitor your site's search performance, submit sitemaps, and identify indexing issues. Use PageSpeed Insights for Core Web Vitals monitoring. Implement structured data and validate it with Google's Rich Results Test. For JavaScript frameworks like React/Vue, consider using Server-Side Rendering (SSR) or Static Site Generation (SSG) with Next.js or Nuxt.js to ensure content is crawlable. Monitor your site weekly and fix issues proactively.