SEO Management
Relevant source files
The following files were used as context for generating this wiki page:The SEO (Search Engine Optimization) management system in this project is a multi-faceted feature designed to maximize the discoverability and search engine ranking of the documentation pages. It leverages the next-seo library for foundational meta tag management and enhances it with a custom <SEO /> component that dynamically generates rich structured data (JSON-LD), breadcrumbs, and other advanced meta tags.
The system integrates with the content layer (contentlayer) to pull page-specific data, automatically generates sitemaps, and even prepares content for consumption by Large Language Models (LLMs) and internal search engines like MeiliSearch. This comprehensive approach ensures that documentation is not only user-friendly but also highly optimized for various discovery platforms.
Core Architecture
The SEO architecture is centered around the <SEO /> component, which is utilized within the main DocsLayout.tsx. This layout wraps every documentation page, ensuring consistent and context-aware SEO treatment across the site.
The following diagram illustrates the data flow from a page request to the final rendered HTML with all SEO enhancements.
This sequence shows how DocsLayout.tsx orchestrates the collection of SEO-relevant data from page content and utilities before passing it to the SEO component for final rendering into the HTML <head>.
Sources: src/layouts/DocsLayout.tsx:100-111, src/components/SEO.tsx
SEO Component
The primary driver for SEO is the <SEO /> React component, which acts as a wrapper around next-seo to provide both default and page-specific meta tags, along with extensive structured data.
Sources: src/components/SEO.tsx
Properties
The component accepts a variety of props to customize the output for each page.
| Prop | Type | Description |
|---|---|---|
title | string | The main title of the page. |
twitterTitle | string | A specific title for Twitter cards. |
description | string | The page description for meta tags. |
image | string | URL for the Open Graph and Twitter image. |
url | string | The canonical URL of the page. |
headers | Header[] | An array of page headers, used for Table of Contents and FAQ schemas. |
breadcrumbs | Array<{name, url}> | An array of breadcrumb objects for navigation and structured data. |
lastModified | string | ISO string of the last modification date. |
author | string | The author of the article. Defaults to "Railway". |
publishedTime | string | ISO string of the publication date. |
Sources: src/components/SEO.tsx:8-16
Default Configuration
A default SEO configuration is applied to all pages, providing baseline information for the site.
// From src/components/SEO.tsx
const config: DefaultSeoProps = {
title: "Railway Docs",
description: "Documentation for Railway",
openGraph: {
type: "website",
url: "https://docs.railway.com",
site_name: "Railway Docs",
images: [{ url: "https://docs.railway.com/og.png" }],
},
twitter: {
site: "@Railway",
handle: "@Railway",
cardType: "summary_large_image",
},
};
Sources: src/components/SEO.tsx:21-36
Structured Data (JSON-LD)
A key feature of the <SEO /> component is its ability to generate multiple types of JSON-LD structured data schemas. This helps search engines understand the content and context of the page, which can lead to rich search results.
The component conditionally builds an array of schema objects and injects them into the page's <head>.
Sources: src/components/SEO.tsx:50-141
Supported Schemas
The following JSON-LD schemas are generated based on the available page data:
| Schema Type | Condition for Generation | Purpose |
|---|---|---|
Organization | Always generated. | Provides information about the Railway organization. |
BreadcrumbList | If breadcrumbs prop has items. | Defines the navigation path to the current page for search results. |
ItemList | If headers prop has items. | Creates a "Table of Contents" schema from the page's headings. |
Article | Always generated. | Describes the page as an article with headline, author, dates, etc. |
FAQPage | If any header in the headers prop ends with a ?. | Structures question-and-answer content for rich snippets like "People also ask". |
Sources: src/components/SEO.tsx:53-141
Dynamic Metadata Utilities
Helper functions in src/utils/seo.ts are used to extract necessary data from the raw markdown and site structure.
extractHeadersFromMarkdown(markdown)
This function parses the raw markdown content of a page to find all headings (#, ##, etc.). It ignores headings within code blocks and generates a sanitized, URL-friendly ID for each. The output is an array of Header objects used to build the ItemList (Table of Contents) and FAQPage schemas.
Sources: src/utils/seo.ts:9-28
buildBreadcrumbs(currentUrl)
This function constructs a breadcrumb trail by traversing the sidebarContent structure defined in src/data/sidebar.ts. It matches the currentUrl against the slugs of pages and subsections to build a hierarchical path from the homepage to the current page. This is used for the BreadcrumbList schema and for generating article:section and article:tag meta tags.
Sources: src/utils/seo.ts:34-106, src/components/SEO.tsx:144-166
Sitemap & Crawlability
The project ensures that search engines can efficiently crawl and index all pages.
Sitemap Generation
A sitemap is automatically generated after every build. The configuration is managed in next-sitemap.config.js.
// next-sitemap.config.js
module.exports = {
siteUrl: process.env.NEXT_PUBLIC_RAILWAY_DOCS_URL || "https://docs.railway.com",
generateRobotsTxt: true,
};
This configuration sets the site's base URL and enables the automatic generation of a robots.txt file. The sitemap generation is triggered by the postbuild script in package.json.
Sources: next-sitemap.config.js:1-4, package.json:7
Content for Search Scrapers
The site is structured with clear CSS selectors to facilitate content scraping for internal search engines like MeiliSearch.
meilisearch-docs-scraper.config.json
{
"index_uid": "docs_railway_app_production",
"start_urls": [
{
"url": "https://docs.railway.com/"
}
],
"selectors": {
"lvl0": ".sidebar .current-section",
"lvl1": ".docs-content h1",
"lvl2": ".docs-content h2",
"lvl3": ".docs-content h3",
"lvl4": ".docs-content h4",
"text": ".docs-content p, .docs-content li, .docs-content a, .docs-content code, .docs-content td, .docs-content pre"
}
}
These selectors define a content hierarchy (lvl0 to lvl4) and the main text content, allowing the scraper to build a structured search index.
Sources: meilisearch-docs-scraper.config.json:1-17
Content for LLMs
An API endpoint at /api/llms-docs.md is provided to serve the entire documentation content as a single, formatted plain text file. This is specifically designed for consumption by Large Language Models (LLMs), enabling them to be trained or queried on the documentation.
The process is as follows:
- All pages are collected from the
sidebarContent. - For each page, the corresponding markdown file is read from
src/docs. - The content is processed to extract the title, description, and text, while stripping out code blocks and markdown formatting for cleaner text.
- All pages are concatenated into a single response.
Sources: src/pages/api/llms-docs.md.ts