Why this matters
Most of the SEO infrastructure on PromptPlan is automatic. When you add a new markdown article under content/learn/, the sitemap picks it up, Open Graph tags are generated, JSON-LD schema is injected, and the canonical URL is set — all without touching any code.
But there are two things that are not automatic and easy to forget. Skipping them means Google either can't find your content, or finds it but doesn't display it well in search results.
---
1. Correct frontmatter on every learn article
Every .md file in content/learn/ must include a complete frontmatter block at the top. The system reads this to generate meta tags, sitemaps, related article links, and navigation.
Required fields
---
title: "Full SEO title — include the main keyword"
description: "A clear summary under 160 characters. This becomes the meta description and the Open Graph description shown when someone shares the link."
slug: "url-slug-matching-the-filename"
category: "getting-started | docs | guides | tutorials | articles | about"
order: 5
tags:
- "tag one"
- "tag two"
keywords:
- "primary keyword"
- "secondary keyword"
relatedArticles:
- "docs/templates-explained"
- "guides/writing-reusable-prompts"
lastUpdated: "YYYY-MM-DD"
---Why each field matters
title — Used as the <title> tag and Open Graph title. Write it as if it's a search result headline. Include the main keyword naturally. Keep it under 60 characters if possible.
description — Becomes the meta description shown under the title in Google results and the preview text when someone shares the link in Slack, Reddit, or Twitter. Be specific and useful, not generic. Under 160 characters.
slug — Must match the filename exactly. This becomes part of the URL: /learn/{category}/{slug}.
category — The section this article belongs to. Must be one of the six valid section slugs.
order — Controls display order within the section. Lower numbers appear first.
keywords — Used in the <meta name="keywords"> tag and in the JSON-LD Article schema. List the 2–4 phrases people would search to find this content.
relatedArticles — Links shown in the sidebar and at the bottom of the article. Format: "section/slug". Drives internal linking which improves SEO.
lastUpdated — Date in YYYY-MM-DD format. Used in the sitemap <lastmod> tag and the JSON-LD dateModified field. Google uses this to assess content freshness. Update it whenever you make substantial edits.
What happens if you skip fields
| Missing field | Consequence |
|---|---|
description | Meta description falls back to a generic placeholder or is omitted entirely |
keywords | JSON-LD schema has no keyword signals for Google |
lastUpdated | Sitemap shows no modification date; Google treats content as stale |
relatedArticles | No internal links from this article to others — weaker link graph |
---
2. Registering new public pages outside /learn
The sitemap and metadata for /learn articles are handled automatically. But if you add a new public page outside the learn section — for example /about, /changelog, /press, or anything else reachable without logging in — you must do two things manually.
Step 1 — Add Open Graph metadata to the page
In your page.tsx, export a metadata object with the full set of fields:
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Page Title | PromptPlan",
description: "Clear, keyword-rich description under 160 characters.",
alternates: {
canonical: "https://promptplan.app/your-route",
},
openGraph: {
title: "Page Title",
description: "Same description as above.",
url: "https://promptplan.app/your-route",
siteName: "PromptPlan",
type: "website",
},
twitter: {
card: "summary",
title: "Page Title",
description: "Same description as above.",
},
};Without the openGraph block, sharing the link anywhere will produce a blank or broken preview. Without alternates.canonical, Google may index multiple versions of the same URL (with and without trailing slash, with query strings, etc.) and split ranking signals across them.
Step 2 — Add an entry to src/app/sitemap.ts
Open src/app/sitemap.ts and add your page to the staticPages array:
{
url: `${BASE_URL}/your-route`,
lastModified: new Date("YYYY-MM-DD"),
changeFrequency: "monthly",
priority: 0.7,
},Choosing priority: This is a hint to Google about relative importance, not a guarantee.
| Priority | Use for |
|---|---|
1.0 | Homepage only |
0.9 | Main hub pages (/learn) |
0.8 | Key marketing pages (/pricing, /about-project) |
0.7 | Section landing pages, secondary marketing |
0.6 | Individual articles |
0.3 | Legal pages (/terms, /privacy, /cookies) |
Choosing changeFrequency: Tells Google how often to re-crawl.
| Value | Use for |
|---|---|
weekly | Pages updated frequently (landing, learn hub) |
monthly | Most content pages |
yearly | Legal and policy pages |
Why sitemap registration matters
Google discovers pages in two ways: following links, and reading the sitemap. A new page with no inbound links from other pages will only be found if it's in the sitemap. Even pages that do have inbound links benefit from sitemap registration because it signals priority and freshness explicitly.
---
Quick reference
| Scenario | What to do |
|---|---|
New article in content/learn/ | Fill in complete frontmatter — everything else is automatic |
New public page outside /learn | Add OG metadata to page.tsx + add entry to sitemap.ts |
| Editing an existing article | Update lastUpdated date in frontmatter |
| Removing a public page | Remove its entry from sitemap.ts |