SEO Guide
This guide covers Search Engine Optimization (SEO) configuration for your SaaS application.
Table of Contents
- Built-in SEO Features
- Page Metadata
- Sitemap Configuration
- Robots.txt Configuration
- Open Graph & Social Sharing
- Structured Data
- Performance Optimization
- SEO Checklist
Built-in SEO Features
The boilerplate includes these SEO features out of the box:
| Feature | File | Description |
|---------|------|-------------|
| Sitemap | /app/sitemap.ts | Auto-generated XML sitemap |
| Robots.txt | /app/robots.ts | Search engine crawl rules |
| Meta tags | /app/layout.tsx | Global meta configuration |
| OG Images | /app/opengraph-image.png | Social sharing images |
| Page metadata | Each page.tsx | Per-page SEO settings |
Page Metadata
Global Metadata
Edit /app/layout.tsx for site-wide defaults:
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: {
default: 'Your SaaS Name',
template: '%s | Your SaaS Name', // Page titles become "Page | Your SaaS Name"
},
description: 'Your main site description for search engines (150-160 characters ideal).',
keywords: ['saas', 'your', 'keywords', 'here'],
authors: [{ name: 'Your Name' }],
creator: 'Your Company',
metadataBase: new URL('https://yourdomain.com'),
openGraph: {
type: 'website',
locale: 'en_US',
url: 'https://yourdomain.com',
siteName: 'Your SaaS Name',
title: 'Your SaaS Name',
description: 'Your description for social sharing.',
images: [
{
url: '/og.png',
width: 1200,
height: 630,
alt: 'Your SaaS Name',
},
],
},
twitter: {
card: 'summary_large_image',
title: 'Your SaaS Name',
description: 'Your description for Twitter.',
images: ['/og.png'],
creator: '@yourusername',
},
robots: {
index: true,
follow: true,
},
};
Per-Page Metadata
Each page can override the defaults:
// /app/pricing/page.tsx
export const metadata = {
title: 'Pricing', // Becomes "Pricing | Your SaaS Name"
description: 'Simple, transparent pricing. Start free, upgrade when you need.',
openGraph: {
title: 'Pricing - Your SaaS Name',
description: 'Choose the perfect plan for your needs.',
},
};
export default function PricingPage() {
// ...
}
Dynamic Metadata
For dynamic pages like blog posts:
// /app/blog/[slug]/page.tsx
import type { Metadata } from 'next';
import { getPostContent } from '@/lib/mdx';
interface Props {
params: { slug: string };
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const post = getPostContent(params.slug);
if (!post) {
return { title: 'Post Not Found' };
}
return {
title: post.title,
description: post.description,
openGraph: {
title: post.title,
description: post.description,
type: 'article',
publishedTime: post.date,
authors: [post.author.name],
images: post.image ? [post.image] : undefined,
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.description,
},
};
}
Sitemap Configuration
The sitemap is automatically generated at /sitemap.xml.
Default Configuration
Edit /app/sitemap.ts to customize:
import { MetadataRoute } from 'next';
import { getAllPosts } from '@/lib/mdx';
export default function sitemap(): MetadataRoute.Sitemap {
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://yourdomain.com';
// Get dynamic content (blog posts)
const posts = getAllPosts();
const blogUrls = posts.map((post) => ({
url: `${baseUrl}/blog/${post.slug}`,
lastModified: new Date(post.date),
changeFrequency: 'monthly' as const,
priority: 0.6,
}));
// Static pages
const staticPages = [
{
url: baseUrl,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
priority: 1,
},
{
url: `${baseUrl}/pricing`,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
priority: 0.9,
},
// Add more pages...
];
return [...staticPages, ...blogUrls];
}
Priority Guidelines
| Priority | Use For | |----------|---------| | 1.0 | Homepage | | 0.9 | Key conversion pages (pricing) | | 0.8 | Important content (blog index) | | 0.6-0.7 | Regular content pages | | 0.5 | Utility pages (login, signup) |
Change Frequency
| Frequency | Use For |
|-----------|---------|
| daily | Frequently updated pages |
| weekly | Regularly updated content |
| monthly | Static content |
| yearly | Rarely changed pages |
Robots.txt Configuration
Edit /app/robots.ts:
import { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://yourdomain.com';
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: [
'/api/', // API routes
'/dashboard/', // Private pages
'/auth/callback', // Auth routes
'/auth/confirm',
'/auth/error',
'/protected/',
],
},
],
sitemap: `${baseUrl}/sitemap.xml`,
};
}
What to Block
Always block:
- API routes (
/api/) - Authenticated areas (
/dashboard/) - Auth callback URLs
- Admin panels
- User-specific pages
Open Graph & Social Sharing
Creating OG Images
-
Static OG Image: Replace
/public/og.png(1200x630 pixels) -
Dynamic OG Images: Use Next.js ImageResponse:
// /app/opengraph-image.tsx
import { ImageResponse } from 'next/og';
export const runtime = 'edge';
export const alt = 'Your SaaS Name';
export const size = { width: 1200, height: 630 };
export const contentType = 'image/png';
export default async function Image() {
return new ImageResponse(
(
<div
style={{
fontSize: 64,
background: 'linear-gradient(to bottom, #000, #111)',
color: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
Your SaaS Name
</div>
),
{ ...size }
);
}
Testing Social Sharing
Use these tools to preview how your pages appear when shared:
- Facebook: Sharing Debugger
- Twitter: Card Validator
- LinkedIn: Post Inspector
Structured Data
Add JSON-LD structured data for rich search results:
Organization Schema
// /app/layout.tsx
export default function RootLayout({ children }) {
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Your SaaS Name',
url: 'https://yourdomain.com',
logo: 'https://yourdomain.com/logo.png',
sameAs: [
'https://twitter.com/yourusername',
'https://github.com/yourusername',
],
};
return (
<html>
<head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
</head>
<body>{children}</body>
</html>
);
}
Product/SaaS Schema
// /app/pricing/page.tsx
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: 'Your SaaS Name',
applicationCategory: 'BusinessApplication',
offers: {
'@type': 'AggregateOffer',
lowPrice: '0',
highPrice: '99',
priceCurrency: 'USD',
offerCount: '3',
},
};
Blog Post Schema
// For blog posts
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.title,
description: post.description,
datePublished: post.date,
author: {
'@type': 'Person',
name: post.author.name,
},
};
Performance Optimization
Page speed is an SEO ranking factor.
Built-in Optimizations
The boilerplate already includes:
- Next.js Image Optimization: Use
next/imagefor automatic optimization - Font Optimization: Google Fonts are self-hosted
- Code Splitting: Automatic per-page code splitting
- Static Generation: Marketing pages are pre-rendered
Additional Tips
-
Optimize images before uploading:
- Use WebP format when possible
- Compress with TinyPNG
- Use appropriate dimensions
-
Lazy load below-fold content:
import dynamic from 'next/dynamic';
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <p>Loading...</p>,
});
- Monitor Core Web Vitals:
- Use PageSpeed Insights
- Check Vercel Analytics
- Monitor in Google Search Console
SEO Checklist
Before Launch
- [ ] Set production URL in
NEXT_PUBLIC_APP_URL - [ ] Update
siteConfigwith real company info - [ ] Create custom OG image (1200x630)
- [ ] Replace favicon and app icons
- [ ] Write unique meta descriptions for key pages
- [ ] Verify sitemap at
/sitemap.xml - [ ] Verify robots.txt at
/robots.txt - [ ] Test OG tags with social sharing debuggers
After Launch
- [ ] Submit sitemap to Google Search Console
- [ ] Submit sitemap to Bing Webmaster Tools
- [ ] Set up Google Analytics
- [ ] Monitor Core Web Vitals
- [ ] Check for crawl errors regularly
Ongoing
- [ ] Create regular blog content
- [ ] Update meta descriptions for new pages
- [ ] Monitor search rankings
- [ ] Fix any reported issues in Search Console
- [ ] Keep sitemap up to date (automatic with Next.js)
File Locations Summary
| File | Purpose |
|------|---------|
| /app/layout.tsx | Global metadata, structured data |
| /app/sitemap.ts | Sitemap generation |
| /app/robots.ts | Crawl rules |
| /app/opengraph-image.png | Default OG image |
| /config/site.ts | Site name, description, URLs |
| /public/favicon.ico | Browser favicon |
| /public/og.png | Social sharing image |
Tools & Resources
Google Tools
- Google Search Console - Monitor search performance
- PageSpeed Insights - Performance testing
- Rich Results Test - Structured data testing
Other Tools
- Ahrefs Webmaster Tools - Free backlink checker
- Screaming Frog - Site auditing
- Schema Markup Validator - Structured data validation