Apps Design System
SEO review — apps/design-system
Audit scope: apps/design-system Next.js App Router metadata, documentation routes, ContentLayer content handling, structured data, headings/semantics, robots/sitemap behavior, and public assets.
Critical findings#
None found.
High severity#
Missing robots and sitemap routes#
Evidence: No robots.* or sitemap.* route exists under apps/design-system/app; find apps/design-system/app -maxdepth 3 \( -name 'robots.*' -o -name 'sitemap.*' \) returned no files. ContentLayer exposes 94 MDX docs, so crawlers have no first-party sitemap for the docs corpus.
Impact: Search engines must discover documentation pages only through navigation links. This weakens crawl coverage for deep component and pattern docs and makes it harder to communicate canonical indexability rules.
Recommended fix: Add app/robots.ts and app/sitemap.ts for the /design-system surface. Generate sitemap entries from allDocs, include the landing page, and use the same canonical origin/base path as metadata.
Open Graph/Twitter images reference missing or inconsistent assets#
Evidence: Root metadata points OG/Twitter images at /design-system/img/assistance-og-image.png (apps/design-system/app/layout.tsx:22-33), but no matching file exists in apps/design-system/public. Docs metadata uses siteConfig.ogImage (apps/design-system/app/(app)/docs/[[...slug]]/page.tsx:53-67), which is configured as https://assistance.bg/design-system/og.jpg (apps/design-system/config/site.ts:1-5), and that asset is also absent from apps/design-system/public.
Impact: Shared links may render without preview images or with 404 image fetches. The root page and docs pages also disagree on the intended image URL.
Recommended fix: Add a real 1200×630 social image asset or App Router opengraph-image/twitter-image route, then point both root and docs metadata to that single asset.
Medium severity#
Canonical URLs are not emitted#
Evidence: Root metadata sets metadataBase and OG URL (apps/design-system/app/layout.tsx:20-26), while docs metadata sets an OG URL (apps/design-system/app/(app)/docs/[[...slug]]/page.tsx:48-53), but neither root nor docs metadata defines alternates.canonical.
Impact: Canonical signals are weaker for a subpath-hosted app (/design-system), especially if the same app is reachable through preview, Pages, or production hostnames.
Recommended fix: Add canonical alternates for the landing page and every doc page. For docs, derive canonical URLs from the final public base URL plus doc.slug.
Docs absolute URLs depend on an unset environment variable#
Evidence: absoluteUrl() returns ${process.env.NEXT_PUBLIC_APP_URL}${path} (apps/design-system/lib/utils.ts:14-16). Docs metadata uses it for openGraph.url (apps/design-system/app/(app)/docs/[[...slug]]/page.tsx:48-53). The audited app has only .env.example, and no local fallback is present in this helper.
Impact: If NEXT_PUBLIC_APP_URL is unset in a deploy, docs OG URLs can render as undefined/docs/..., creating invalid social metadata.
Recommended fix: Replace this helper with a single site URL source that defaults to siteConfig.url or the same origin/base path used by metadataBase, then use it for OG URLs, canonicals, robots, and sitemap.
Structured data is absent#
Evidence: No JSON-LD or schema.org output was found by rg -n "json-ld|application/ld\\+json|Schema.org|schema.org" apps/design-system. Breadcrumb UI exists in the docs route (apps/design-system/app/(app)/docs/[[...slug]]/page.tsx:92-119), but no BreadcrumbList, TechArticle, SoftwareSourceCode, or WebSite schema is emitted.
Impact: Rich-result eligibility and machine-readable context for documentation pages are limited.
Recommended fix: Add minimal JSON-LD for docs pages: BreadcrumbList from breadcrumbSegments and TechArticle or Article with title, description, URL, and publisher. Add WebSite/Organization schema at the app shell if this surface is intended to rank independently.
Unpublished ContentLayer docs are not excluded#
Evidence: ContentLayer defines a published field with default true (apps/design-system/contentlayer.config.js:75-78), but getDocFromParams, generateStaticParams, and the page render all read directly from allDocs without filtering (apps/design-system/app/(app)/docs/[[...slug]]/page.tsx:26-34, 72-75).
Impact: Any future published: false MDX file would still receive a static route and metadata, making drafts indexable if linked or discovered.
Recommended fix: Filter allDocs to doc.published !== false for route generation, lookups, navigation/search inputs, sitemap, and any future structured data.
Low severity#
Heading and landmark semantics are generally sound, with one homepage refinement#
Evidence: The app shell wraps content in a single <main> (apps/design-system/app/(app)/layout.tsx:17-26). The homepage has one <h1> followed by an <h2> tagline (apps/design-system/app/(app)/page.tsx:11-15). Docs pages render one page <h1> from ContentLayer title (apps/design-system/app/(app)/docs/[[...slug]]/page.tsx:120-127) and MDX heading components preserve semantic heading tags (apps/design-system/components/mdx-components.tsx:55-83).
Impact: The homepage tagline is visually a subtitle rather than a document section. This is minor, but using <p> would produce a cleaner outline.
Recommended fix: Change the homepage tagline from <h2> to a styled paragraph unless it becomes the heading for a real subsection.
Positive observations#
- Root metadata includes title, description, metadata base, favicon metadata, and OG/Twitter blocks (
apps/design-system/app/layout.tsx:15-33). - Docs pages generate per-document title, description, OG, and Twitter metadata from ContentLayer frontmatter (
apps/design-system/app/(app)/docs/[[...slug]]/page.tsx:37-69). - ContentLayer requires
titleanddescriptionfor every doc (apps/design-system/contentlayer.config.js:67-74), giving metadata a consistent source. - MDX heading IDs and section anchors are generated through
rehypeSlugandrehypeAutolinkHeadings(apps/design-system/contentlayer.config.js:117-118andapps/design-system/contentlayer.config.js:197-205). - Deployed header rules explicitly allow indexing on the production domain and block common preview domains (
apps/design-system/public/_headers:1-4,25-33).