← All Articles
Web Development5 min read

Next.js SEO in 2025: The Complete Checklist I Use on Every Project

Youness Haji

Youness Haji

February 20, 2025

Next.js gives you the tools for great SEO. But "tools available" and "SEO done right" are very different things. After shipping dozens of Next.js projects, I've built a checklist I run on every single one. Here it is — the complete list with code examples.

Why Next.js SEO Isn't Automatic

Out of the box, Next.js gives you server-side rendering, which is a head start. But Google's crawlers look for a lot more than rendered HTML. They want structured data, proper metadata, fast load times, mobile optimization, and internal linking.

None of that comes free with npx create-next-app.

The 15-Point Checklist

1. Metadata API: Title Templates

Every page needs a unique title. The Metadata API makes this clean:

typescript
// app/layout.tsx
export const metadata: Metadata = {
  title: {
    default: 'Youness Haji — Full-Stack Developer',
    template: '%s | Youness Haji',
  },
  description: 'Full-stack developer specializing in AI-powered web and mobile apps.',
};

The template pattern means child pages only need to set title: 'About' and it becomes "About | Youness Haji" automatically.

2. Open Graph Images

Every page shared on LinkedIn or Twitter needs an OG image. Generate them dynamically:

typescript
// app/api/og/route.tsx
import { ImageResponse } from '@vercel/og';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const title = searchParams.get('title') || 'Youness Haji';

  return new ImageResponse(
    <div style={{ background: '#04050F', color: 'white', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      <h1 style={{ fontSize: 60 }}>{title}</h1>
    </div>,
    { width: 1200, height: 630 }
  );
}

Then reference it in your metadata:

typescript
openGraph: {
  images: [{ url: '/api/og?title=My Page Title', width: 1200, height: 630 }],
}

3. JSON-LD Structured Data

Google uses structured data to create rich results. For a developer portfolio, Person schema is essential:

typescript
const personJsonLd = {
  '@context': 'https://schema.org',
  '@type': 'Person',
  name: 'Youness Haji',
  jobTitle: 'Full-Stack Developer',
  url: 'https://younesshaji.com',
  sameAs: [
    'https://github.com/YounessHajiDev',
    'https://linkedin.com/in/younesshaji',
  ],
};

For blog posts, use Article schema. For projects, use SoftwareApplication.

4. Sitemap Generation

Next.js 14 makes this native:

typescript
// app/sitemap.ts
export default function sitemap(): MetadataRoute.Sitemap {
  const posts = getAllPosts();
  return [
    { url: 'https://younesshaji.com', lastModified: new Date(), priority: 1 },
    { url: 'https://younesshaji.com/blog', lastModified: new Date(), priority: 0.9 },
    ...posts.map((post) => ({
      url: `https://younesshaji.com/blog/${post.slug}`,
      lastModified: new Date(post.date),
      priority: 0.8,
    })),
  ];
}

5. Robots.txt

typescript
// app/robots.ts
export default function robots(): MetadataRoute.Robots {
  return {
    rules: { userAgent: '*', allow: '/' },
    sitemap: 'https://younesshaji.com/sitemap.xml',
  };
}

6. Canonical URLs

Every page must have a canonical URL to prevent duplicate content issues:

typescript
alternates: {
  canonical: 'https://younesshaji.com/blog/my-post',
}

7. Core Web Vitals

Google measures three metrics: LCP (Largest Contentful Paint), FID (First Input Delay), and CLS (Cumulative Layout Shift).

Quick wins:

  • Use next/image with explicit dimensions to prevent CLS
  • Use next/font for font loading (prevents FOUT)
  • Lazy load below-the-fold components with dynamic()

8. Image Optimization

Always use the Next.js <Image> component:

typescript
<Image
  src="/project-screenshot.png"
  alt="Descriptive alt text for accessibility and SEO"
  width={800}
  height={450}
  priority={isAboveFold}
/>

The alt attribute isn't optional. It's how Google understands your images.

9. Internal Linking Architecture

Every page should link to at least 2 other pages on your site. This distributes "link juice" and helps Google discover all your content.

My rule: every blog post links to at least one project and ends with a link to contact.

10. Meta Descriptions

Every page needs a unique description under 155 characters. This is what shows up in Google search results.

typescript
description: 'Learn how to build AI-powered apps with Next.js and Groq. Step-by-step tutorial with code examples.',

11. Heading Hierarchy

Use exactly one <h1> per page. Structure content with <h2> and <h3> tags. Google uses headings to understand page structure.

12. URL Structure

Clean, descriptive URLs beat random slugs:

  • /blog/nextjs-seo-checklist-2025 (descriptive)
  • /blog/post-123 (meaningless to Google)

13. Mobile Optimization

Google uses mobile-first indexing. Test every page on mobile viewport. Use responsive design, not separate mobile pages.

14. Page Speed

Use next build and check the output sizes. Pages over 200KB of JavaScript should be code-split. Use dynamic() imports for heavy components.

15. hreflang for Multilingual Sites

If your site supports multiple languages (like mine with EN/FR):

typescript
alternates: {
  languages: {
    'en': '/en',
    'fr': '/fr',
  },
}

The Quick Audit

Run this after every deploy:

  1. Google Search Console — submit sitemap, check for errors
  2. Lighthouse — aim for 90+ on all metrics
  3. curl -I https://yousite.com — check response headers
  4. Share a page on LinkedIn — verify OG image shows correctly
  5. Search site:yousite.com on Google — verify pages are indexed

Conclusion

SEO isn't a one-time task. It's a system you build into your development workflow. This checklist is my system. Fork it, adapt it, and run it on every project.

The best time to add SEO was at the start of the project. The second best time is now.


Want to see this checklist in action? Check out how I implemented it on this very portfolio. Need SEO help on your Next.js project? Let's talk.