Apps Ui Library
SEO review: apps/ui-library
Scope: apps/ui-library Next.js app, including App Router metadata, docs/registry routes, ContentLayer content, structured data, robots/sitemap behavior, headings/content semantics, and public assets.
Summary#
apps/ui-library has a solid baseline for title/description metadata, Open Graph/Twitter images, generated ContentLayer docs pages, semantic article headings, favicon assets, registry JSON assets, and llms.txt. The main SEO gaps are crawl discovery/control and canonical URL consistency, especially because the app appears to live under /ui while metadata is split between metadataBase, NEXT_PUBLIC_BASE_PATH, and NEXT_PUBLIC_APP_URL.
High severity#
Missing robots.txt and sitemap generation#
Evidence
- No
robots.*orsitemap.*App Router metadata files exist underapps/ui-library/app. - No
robots.txtorsitemap.xmlfiles exist underapps/ui-library/public. - The build scripts generate registry JSON and
llms.txt, but no sitemap/robots artifact:apps/ui-library/package.json:9-12. - Cloudflare/Vercel headers only set
X-Robots-Tagfor specific hosts and assets; they do not publish crawl entrypoints:apps/ui-library/public/_headers:1-27.
Impact
Search engines have no app-local sitemap listing the generated docs routes, registry/docs URLs, or canonical app base. Preview blocking is header-based only, and production crawl policy depends on deployment host matching _headers.
Recommended fix
Add app/robots.ts and app/sitemap.ts that use the same canonical base URL and base path as metadata. Include the home page and all published ContentLayer docs pages; exclude /example/*, preview-only hosts, and generated registry JSON unless those assets are intentionally indexable.
Canonical URLs are not emitted for home or docs pages#
Evidence
- Root metadata defines
metadataBase, title/description, Open Graph, and Twitter metadata, but noalternates.canonical:apps/ui-library/app/layout.tsx:17-37. - Docs metadata overrides title/description and Open Graph URL, but also omits
alternates.canonical:apps/ui-library/app/(app)/docs/[[...slug]]/page.tsx:37-57. - ContentLayer produces stable slugs for every doc (
/${doc._raw.flattenedPath}), but those slugs are only used for route matching and OG URL generation:apps/ui-library/contentlayer.config.js:11-20.
Impact
The same content can be reachable through different deployment hosts, with or without /ui, or through framework-specific near-duplicate pages. Without canonical tags, search engines must infer the preferred URL.
Recommended fix
Centralize a siteUrl/basePath helper and set alternates.canonical in root and docs metadata. For docs pages, canonical should resolve from each doc.slug using the production URL, not from a request-time or optional env value.
Medium severity#
Absolute URL generation can produce invalid metadata if NEXT_PUBLIC_APP_URL is unset or mismatched#
Evidence
- Docs Open Graph URL uses
absoluteUrl(doc.slug):apps/ui-library/app/(app)/docs/[[...slug]]/page.tsx:49-55. absoluteUrlconcatenatesprocess.env.NEXT_PUBLIC_APP_URLdirectly:apps/ui-library/lib/utils.ts:5-7.- Root metadata separately hard-codes
metadataBase: new URL('https://assistance.bg')and derives OG image paths fromNEXT_PUBLIC_BASE_PATH:apps/ui-library/app/layout.tsx:15-27. llms.txtassumes production docs live athttps://assistance.bg/ui/docs:apps/ui-library/scripts/build-llms-txt.ts:4.
Impact
If NEXT_PUBLIC_APP_URL is absent, stale, or does not include /ui, docs pages can emit malformed or non-canonical og:url values while other assets point at https://assistance.bg/ui/....
Recommended fix
Use one canonical URL source for metadataBase, absoluteUrl, sitemap, robots, and llms.txt. Fail builds when the production URL is missing, or provide a checked default that includes the intended base path.
Draft/unpublished ContentLayer documents are not filtered#
Evidence
- ContentLayer supports a
publishedfield with defaulttrue:apps/ui-library/contentlayer.config.js:69-72. - Docs route lookup searches all docs without checking
published:apps/ui-library/app/(app)/docs/[[...slug]]/page.tsx:26-34. - Static params include every doc from
allDocs:apps/ui-library/app/(app)/docs/[[...slug]]/page.tsx:60-64. llms.txtrecursively includes every.mdxfile with a title and does not parse/filterpublished:apps/ui-library/scripts/build-llms-txt.ts:47-75.
Impact
Any future published: false draft would still be statically generated, discoverable in llms.txt, and eligible for sitemap inclusion if a sitemap later mirrors the current route behavior.
Recommended fix
Create a shared publishedDocs = allDocs.filter((doc) => doc.published) helper and use it for route lookup, static params, sitemap, navigation/search where applicable, and llms.txt generation.
Home page is forced dynamic with no revalidation#
Evidence
- The home route exports
dynamic = 'force-dynamic'andrevalidate = 0:apps/ui-library/app/(app)/page.tsx:10-11. - The dynamic behavior appears to be driven by a randomized demo room name:
apps/ui-library/app/(app)/page.tsx:13-15.
Impact
The landing page cannot be fully statically optimized and may be less cacheable for crawlers. The random demo URL also changes rendered markup between requests.
Recommended fix
Move randomized demo state into a client-only demo component or use a deterministic room identifier so the index page can be statically rendered and cached.
No structured data for docs or component registry pages#
Evidence
- No
application/ld+json,schema.org, or JSON-LD structured data references were found inapps/ui-library. - Docs pages render rich doc content with title, description, breadcrumbs, TOC, and source panel, but no
Article,TechArticle,BreadcrumbList, orSoftwareSourceCodeschema:apps/ui-library/app/(app)/docs/[[...slug]]/page.tsx:76-111.
Impact
Search engines can index the pages, but the app misses enhanced understanding for technical documentation, breadcrumbs, and reusable component/code resources.
Recommended fix
Add per-doc JSON-LD for TechArticle and BreadcrumbList. Consider SoftwareSourceCode or CreativeWork for registry-backed component pages where source metadata is available.
Low severity#
OG/Twitter metadata exists but is generic#
Evidence
- Root metadata sets a static OG/Twitter image at
${BASE_PATH}/img/assistance-og-image.png:apps/ui-library/app/layout.tsx:23-35. - The image asset exists at
apps/ui-library/public/img/assistance-og-image.pngand is the correct 1200x630 size. - Docs metadata overrides title/description but inherits the same generic image:
apps/ui-library/app/(app)/docs/[[...slug]]/page.tsx:45-56.
Impact
Social previews should work, but every docs page shares the same image and root publishedTime/modifiedTime values are generated at runtime/build time rather than derived from content: apps/ui-library/app/layout.tsx:28-29.
Recommended fix
Keep the current image as a fallback, but add deterministic per-doc OG images or remove article timestamps unless real content dates are available.
Footer section headings skip heading levels#
Evidence
- The footer announces a visually hidden
<h2>and then renders footer section titles as<h6>:apps/ui-library/components/site-footer.tsx:33-45. - The main docs page uses one visible
<h1>for the document title before MDX content:apps/ui-library/app/(app)/docs/[[...slug]]/page.tsx:84-98.
Impact
This is mostly an accessibility/semantics issue, but cleaner heading order helps crawlers and assistive technology understand page structure.
Recommended fix
Use non-heading text for footer column labels or change them to <h3> under the footer <h2>.
Positive findings#
- Root layout sets
lang="en", application name, title, description, metadata base, favicon metadata, Open Graph, and Twitter card metadata:apps/ui-library/app/layout.tsx:17-37andapps/ui-library/app/layout.tsx:49-65. - Docs pages derive metadata from required ContentLayer frontmatter fields:
apps/ui-library/contentlayer.config.js:56-68andapps/ui-library/app/(app)/docs/[[...slug]]/page.tsx:45-57. - Static params are generated for ContentLayer docs, enabling pre-rendering of docs routes:
apps/ui-library/app/(app)/docs/[[...slug]]/page.tsx:60-64. - MDX rendering preserves semantic headings, paragraphs, lists, tables, blockquotes, and image
altattributes from content:apps/ui-library/components/mdx-components.tsx:28-117. - Preview deployments are protected from indexing through
X-Robots-Tag: noindex, nofollow:apps/ui-library/public/_headers:20-27. llms.txtis generated and published as a machine-readable docs index:apps/ui-library/scripts/build-llms-txt.ts:78-106.