Buy Template Now $65
Skip to main content

SEO


The template includes a scalable SEO architecture built on the Next.js App Router metadata API.

Features

  • Global SEO configuration
  • Page-specific metadata
  • Dynamic metadata generation
  • Open Graph support
  • Twitter cards
  • Structured data (JSON-LD)
  • Sitemap generation
  • Robots.txt generation
  • Blog SEO
  • Docs SEO
  • Dynamic route SEO

Folder Structure

src/
├── app/
│   ├── layout.tsx
│   ├── sitemap.ts
│   ├── robots.ts
│   ├── pricing/
│   │   └── page.tsx
│   ├── blog/
│   │   └── [slug]/
│   │       └── page.tsx
│   └── docs/
│       └── [slug]/
│           └── page.tsx
│
├── content/
│   ├── marketing/
│   │   ├── home.ts
│   │   ├── pricing.ts
│   │   └── features.ts
│   ├── blog/
│   ├── docs/
│   └── seo/
│       └── global.ts
│
├── lib/
│   └── seo/
│       ├── generateMetadata.ts
│       └── schema.ts
│
└── components/
    └── seo/
        └── StructuredData.tsx

Global SEO Configuration

Global SEO settings live in a single file.

content/seo/global.ts

export const SEO_CONFIG = {
  /** Site name used in titles, metadata, and Open Graph branding. */
  siteName: "Pulse AI",
 
  /** Base URL. Falls back to localhost for local development. */
  siteUrl: process.env.NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000",
 
  /** Default title shown in browser tabs and search results. */
  title: "Pulse AI",
 
  /** Meta description shown in search results. */
  description: "Premium Next.js SAAS Template - Built by launchpadnext",
 
  /** Default Open Graph image. Recommended size: 1200x630px. */
  ogImage: "/og/default.png",
 
  /** Twitter handle for Twitter Card metadata. */
  twitterHandle: "@launchpadnext",
 
  /** SEO keywords for internal indexing or non-Google engines. */
  keywords: ["Next.js", "SaaS", "AI", "Boilerplate", "Templates"],
 
  author: "launchpadnext",
  creator: "launchpadnext",
  twitter: "@launchpadnext",
 
  /** Default locale for Open Graph and international SEO. */
  locale: "en_US",
};

Root Layout Metadata

Global metadata is defined in app/layout.tsx and acts as a fallback for all pages.

app/layout.tsx

import type { Metadata } from "next";
import { SEO_CONFIG } from "@/content/seo/global";
 
export const metadata: Metadata = {
  metadataBase: new URL(SEO_CONFIG.siteUrl),
 
  title: {
    default: SEO_CONFIG.title,
    template: `%s | ${SEO_CONFIG.siteName}`,
  },
 
  description: SEO_CONFIG.description,
 
  openGraph: {
    title: SEO_CONFIG.title,
    description: SEO_CONFIG.description,
    images: [SEO_CONFIG.ogImage],
    siteName: SEO_CONFIG.siteName,
    type: "website",
  },
 
  twitter: {
    card: "summary_large_image",
    title: SEO_CONFIG.title,
    description: SEO_CONFIG.description,
    images: [SEO_CONFIG.ogImage],
  },
};

Page SEO

Each page defines its own metadata alongside its content in a single file.

content/marketing/pricing.ts

export const PRICING_CONTENT = {
  seo: {
    title: "Pricing",
    description: "Simple pricing plans for startups and growing teams.",
    keywords: ["pricing", "saas pricing", "subscription plans"],
  },
 
  // Main content
  hero: {
    heading: "Simple Pricing",
  },
};

app/pricing/page.tsx

import { Metadata } from "next";
import { PRICING_CONTENT } from "@/content/marketing/pricing";
 
export const metadata: Metadata = {
  title: PRICING_CONTENT.seo.title,
  description: PRICING_CONTENT.seo.description,
  keywords: PRICING_CONTENT.seo.keywords,
};
 
export default function PricingPage() {
  return <Pricing />;
}

Reusable Metadata Generator

For larger projects, use a helper to avoid repeating Open Graph and Twitter metadata on every page.

lib/seo/generateMetadata.ts

import { Metadata } from "next";
 
interface SeoProps {
  title: string;
  description: string;
  keywords?: string[];
  image?: string;
}
 
export function generateMetadata({
  title,
  description,
  keywords,
  image,
}: SeoProps): Metadata {
  return {
    title,
    description,
    keywords,
    openGraph: {
      title,
      description,
      images: image ? [image] : undefined,
    },
    twitter: {
      card: "summary_large_image",
      title,
      description,
      images: image ? [image] : undefined,
    },
  };
}

Usage:

import { generateMetadata } from "@/lib/seo/generateMetadata";
import { PRICING_CONTENT } from "@/content/marketing/pricing";
 
export const metadata = generateMetadata(PRICING_CONTENT.seo);

Dynamic Route SEO

Next.js supports metadata generation at runtime using generateMetadata. This is useful for blog posts, documentation pages, integrations, changelogs, and case studies.

Blog SEO

Blog content:

export const BLOG_POST = {
  slug: "best-ai-chatbots",
  title: "Best AI Chatbots",
  description: "Discover the best AI chatbots available today.",
  image: "/blog/best-ai-chatbots.png",
  publishedAt: "2026-01-10",
};

app/blog/[slug]/page.tsx

import { generateMetadata } from "@/lib/seo/generateMetadata";
 
export async function generateMetadata({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const post = await getPostBySlug(slug);
 
  return generateMetadata({
    title: post.title,
    description: post.description,
    image: post.image,
  });
}
 
export default async function BlogPage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const post = await getPostBySlug(slug);
 
  return <BlogPost post={post} />;
}

Docs SEO

app/docs/[slug]/page.tsx

import { generateMetadata } from "@/lib/seo/generateMetadata";
 
export async function generateMetadata({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const doc = await getDocBySlug(slug);
 
  return generateMetadata({
    title: doc.title,
    description: doc.description,
  });
}
 
export default async function DocsPage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const doc = await getDocBySlug(slug);
 
  return <DocsContent doc={doc} />;
}

Structured Data

Structured data helps search engines better understand your content.

components/seo/StructuredData.tsx

interface StructuredDataProps {
  data: Record<string, unknown>;
}
 
export default function StructuredData({ data }: StructuredDataProps) {
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
    />
  );
}

lib/seo/schema.ts

export function softwareSchema() {
  return {
    "@context": "https://schema.org",
    "@type": "SoftwareApplication",
    name: "Pulse AI",
    applicationCategory: "BusinessApplication",
    operatingSystem: "Web",
  };
}

Usage:

import StructuredData from "@/components/seo/StructuredData";
import { softwareSchema } from "@/lib/seo/schema";
 
<StructuredData data={softwareSchema()} />;

Sitemap

The sitemap is automatically generated at build time.

app/sitemap.ts

import { MetadataRoute } from "next";
 
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await getAllPosts();
 
  return [
    { url: "https://pulseai.com", priority: 1 },
    { url: "https://pulseai.com/pricing", priority: 0.9 },
    ...posts.map((post) => ({
      url: `https://pulseai.com/blog/${post.slug}`,
      priority: 0.7,
    })),
  ];
}

Robots

app/robots.ts

import { MetadataRoute } from "next";
 
export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: "*",
      allow: "/",
    },
    sitemap: "https://pulseai.com/sitemap.xml",
  };
}

content/
├── marketing/
│   ├── home.ts
│   ├── pricing.ts
│   ├── features.ts
│   ├── about.ts
│   └── contact.ts
├── blog/
├── docs/
├── legal/
└── seo/
    └── global.ts

Each page exports both its content and SEO configuration from a single file, keeping metadata, copy, and page config together:

// content/marketing/pricing.ts
 
export const PRICING_CONTENT = {
  seo: {
    title: "Pricing",
    description: "Simple pricing plans for startups and growing teams.",
    keywords: ["Next.js 16 Template", "SaaS"],
    ogImage: "/images/og/pricing.png",
  },
  // ...page content
};

Best Practices

  • Keep SEO content close to page content
  • Always provide a unique title and description per page
  • Use Open Graph images for important pages
  • Add structured data for blogs and SaaS products
  • Generate metadata dynamically for all dynamic routes
  • Keep titles under 60 characters where possible
  • Keep descriptions between 120–160 characters
  • Include relevant keywords naturally within page content
  • Ensure all important pages are included in the sitemap