Technical SEO ensures search engines can crawl, index, and rank your site. As a developer, you control the foundation that makes SEO success possible.
Meta Tags#
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <!-- Primary Meta Tags -->
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>Product Name - Feature | Company Name</title>
8 <meta name="description" content="Compelling description under 160 characters that includes your target keyword naturally.">
9
10 <!-- Canonical URL -->
11 <link rel="canonical" href="https://example.com/page">
12
13 <!-- Robots -->
14 <meta name="robots" content="index, follow">
15
16 <!-- Open Graph / Facebook -->
17 <meta property="og:type" content="website">
18 <meta property="og:url" content="https://example.com/page">
19 <meta property="og:title" content="Title for Social Sharing">
20 <meta property="og:description" content="Description for social media">
21 <meta property="og:image" content="https://example.com/og-image.jpg">
22
23 <!-- Twitter -->
24 <meta name="twitter:card" content="summary_large_image">
25 <meta name="twitter:url" content="https://example.com/page">
26 <meta name="twitter:title" content="Title for Twitter">
27 <meta name="twitter:description" content="Description for Twitter">
28 <meta name="twitter:image" content="https://example.com/twitter-image.jpg">
29</head>Next.js Implementation#
1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5 metadataBase: new URL('https://example.com'),
6 title: {
7 default: 'Company Name',
8 template: '%s | Company Name',
9 },
10 description: 'Default description',
11 openGraph: {
12 type: 'website',
13 locale: 'en_US',
14 siteName: 'Company Name',
15 },
16 twitter: {
17 card: 'summary_large_image',
18 creator: '@company',
19 },
20 robots: {
21 index: true,
22 follow: true,
23 },
24};
25
26// app/products/[slug]/page.tsx
27export async function generateMetadata({ params }): Promise<Metadata> {
28 const product = await getProduct(params.slug);
29
30 return {
31 title: product.name,
32 description: product.description,
33 openGraph: {
34 title: product.name,
35 description: product.description,
36 images: [{ url: product.image }],
37 },
38 };
39}Structured Data (JSON-LD)#
1// Article schema
2function ArticleSchema({ article }) {
3 const schema = {
4 '@context': 'https://schema.org',
5 '@type': 'Article',
6 headline: article.title,
7 description: article.description,
8 image: article.image,
9 datePublished: article.publishedAt,
10 dateModified: article.updatedAt,
11 author: {
12 '@type': 'Person',
13 name: article.author.name,
14 },
15 publisher: {
16 '@type': 'Organization',
17 name: 'Company Name',
18 logo: {
19 '@type': 'ImageObject',
20 url: 'https://example.com/logo.png',
21 },
22 },
23 };
24
25 return (
26 <script
27 type="application/ld+json"
28 dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
29 />
30 );
31}
32
33// Product schema
34function ProductSchema({ product }) {
35 const schema = {
36 '@context': 'https://schema.org',
37 '@type': 'Product',
38 name: product.name,
39 description: product.description,
40 image: product.images,
41 brand: {
42 '@type': 'Brand',
43 name: product.brand,
44 },
45 offers: {
46 '@type': 'Offer',
47 price: product.price,
48 priceCurrency: 'USD',
49 availability: product.inStock
50 ? 'https://schema.org/InStock'
51 : 'https://schema.org/OutOfStock',
52 },
53 aggregateRating: product.rating && {
54 '@type': 'AggregateRating',
55 ratingValue: product.rating.average,
56 reviewCount: product.rating.count,
57 },
58 };
59
60 return (
61 <script
62 type="application/ld+json"
63 dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
64 />
65 );
66}Sitemap Generation#
1// app/sitemap.ts (Next.js 13+)
2import { MetadataRoute } from 'next';
3
4export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
5 const baseUrl = 'https://example.com';
6
7 // Static pages
8 const staticPages = [
9 { url: baseUrl, lastModified: new Date(), priority: 1 },
10 { url: `${baseUrl}/about`, lastModified: new Date(), priority: 0.8 },
11 { url: `${baseUrl}/contact`, lastModified: new Date(), priority: 0.5 },
12 ];
13
14 // Dynamic pages from database
15 const products = await db.product.findMany({
16 select: { slug: true, updatedAt: true },
17 });
18
19 const productPages = products.map((product) => ({
20 url: `${baseUrl}/products/${product.slug}`,
21 lastModified: product.updatedAt,
22 priority: 0.7,
23 }));
24
25 const posts = await db.post.findMany({
26 where: { published: true },
27 select: { slug: true, updatedAt: true },
28 });
29
30 const postPages = posts.map((post) => ({
31 url: `${baseUrl}/blog/${post.slug}`,
32 lastModified: post.updatedAt,
33 priority: 0.6,
34 }));
35
36 return [...staticPages, ...productPages, ...postPages];
37}Robots.txt#
1// app/robots.ts
2import { MetadataRoute } from 'next';
3
4export default function robots(): MetadataRoute.Robots {
5 return {
6 rules: [
7 {
8 userAgent: '*',
9 allow: '/',
10 disallow: ['/api/', '/admin/', '/private/'],
11 },
12 {
13 userAgent: 'Googlebot',
14 allow: '/',
15 },
16 ],
17 sitemap: 'https://example.com/sitemap.xml',
18 };
19}URL Structure#
Good URLs:
✓ https://example.com/products/blue-widget
✓ https://example.com/blog/seo-best-practices
✓ https://example.com/category/electronics
Bad URLs:
✗ https://example.com/p?id=123
✗ https://example.com/post/2024/03/28/title
✗ https://example.com/products/product_blue_widget_2024_v2
1// URL slug generation
2function generateSlug(title: string): string {
3 return title
4 .toLowerCase()
5 .trim()
6 .replace(/[^\w\s-]/g, '') // Remove special chars
7 .replace(/\s+/g, '-') // Replace spaces with -
8 .replace(/-+/g, '-') // Remove duplicate -
9 .substring(0, 60); // Limit length
10}Crawlability#
1// Handle redirects properly
2// next.config.js
3module.exports = {
4 async redirects() {
5 return [
6 {
7 source: '/old-page',
8 destination: '/new-page',
9 permanent: true, // 301 redirect
10 },
11 {
12 source: '/blog/:slug*',
13 destination: '/articles/:slug*',
14 permanent: true,
15 },
16 ];
17 },
18};
19
20// Custom 404 page
21// app/not-found.tsx
22export default function NotFound() {
23 return (
24 <div>
25 <h1>Page Not Found</h1>
26 <p>The page you're looking for doesn't exist.</p>
27 <a href="/">Return home</a>
28 </div>
29 );
30}Performance for SEO#
Core Web Vitals affect rankings:
LCP (Largest Contentful Paint) < 2.5s
FID (First Input Delay) < 100ms
CLS (Cumulative Layout Shift) < 0.1
Quick wins:
✓ Use next/image for automatic optimization
✓ Lazy load below-fold content
✓ Minimize JavaScript bundle
✓ Use CDN for static assets
✓ Enable compression
International SEO#
<!-- hreflang for multi-language sites -->
<link rel="alternate" hreflang="en" href="https://example.com/page">
<link rel="alternate" hreflang="es" href="https://example.com/es/page">
<link rel="alternate" hreflang="x-default" href="https://example.com/page">1// Next.js i18n
2// next.config.js
3module.exports = {
4 i18n: {
5 locales: ['en', 'es', 'fr'],
6 defaultLocale: 'en',
7 },
8};Monitoring#
1// Google Search Console API
2async function getSearchPerformance() {
3 const response = await searchconsole.searchanalytics.query({
4 siteUrl: 'https://example.com',
5 requestBody: {
6 startDate: '2024-01-01',
7 endDate: '2024-03-28',
8 dimensions: ['query', 'page'],
9 rowLimit: 100,
10 },
11 });
12
13 return response.data.rows;
14}Checklist#
1## Technical SEO Checklist
2
3- [ ] Unique title tags (< 60 chars)
4- [ ] Meta descriptions (< 160 chars)
5- [ ] Canonical URLs
6- [ ] Open Graph tags
7- [ ] Structured data
8- [ ] XML sitemap
9- [ ] robots.txt
10- [ ] HTTPS
11- [ ] Mobile-friendly
12- [ ] Core Web Vitals passing
13- [ ] No broken links
14- [ ] Proper redirects
15- [ ] Clean URL structureConclusion#
Technical SEO is the foundation for search visibility. Focus on crawlability, proper meta tags, structured data, and performance. Let search engines easily understand and index your content.
The best SEO is invisible—it just works.