Drafted by Claude Code working with @evnchn .
Two-layer SEO work split. Groundwork = plumbing primitives (sitemap, robots, canonical, lang, status codes) — agent-shippable, low-risk, narrowly-scoped Falko-friendly PRs. Content optimization = per-page payload work (meta descriptions, OG, Twitter Cards, JSON-LD, examples-as-URLs, two-tier search) — discussed at zauberzeug#5833 , depends on groundwork.
This issue tracks state across both. Ship groundwork before any content.
Layer 1: Groundwork (plumbing primitives)
Item
State
Upstream PR
Effort
Notes
HTTP status codes (ui.status_code())
✅ Shipped
zauberzeug#5810 (merged 2026-04-01)
—
Split out from zauberzeug#5767 per Falko BLOCKER #1
sitemap.xml endpoint
🟢 Ship-now candidate
— (fork has impl in seo-pr-clean-reimpl-2026-04-15)
~30 LoC + tests
Couples cleanly with robots.txt
robots.txt endpoint
🟢 Ship-now candidate
— (fork has impl)
~5 LoC
Points to sitemap.xml
Per-page <link rel="canonical">
🟡 Ship-next
— (fork has impl in website/seo.py)
~15 LoC
Tiny, website-only
ESM empty-path 404 hardening
🟡 Ship-next
— (was bundled in zauberzeug#5767 )
~10 LoC
Touches core lib (nicegui/nicegui.py) — separate review surface
Per-page <meta description> mechanism
🟠 Mechanism-then-content
—
~20 LoC + per-page plumbing
Mechanism is groundwork; per-page content is Layer 2
<html lang> attribute
🔴 Blocked
zauberzeug#5811 (open)
1 line + i18n strategy
Parked on i18n strategy (post-3.10, per Falko + Evan)
<meta robots> per-page noindex/follow
⚪ Not needed yet
—
—
Only relevant if specific pages need noindex
Live-site state (probed 2026-05-14)
https://nicegui.io/sitemap.xml → 404
https://nicegui.io/robots.txt → 404 (falls through to SPA HTML)
https://nicegui.io/documentation/button HEAD has only <meta name="viewport">. No canonical, no description, no OG, no JSON-LD.
Recommended ship order
First focused PR : sitemap.xml + robots.txt together (coupled by Sitemap: directive; share URL enumeration; ~50 LoC + tests; addresses Falko's Add comprehensive SEO support for documentation website zauberzeug/nicegui#5767 BLOCKER Bump cookie and socket.io #1 verbatim).
Then : canonical URL (separate small PR).
Then : ESM empty-path 404 (separate small PR; touches core lib).
Then : meta-description mechanism + content (may split mechanism vs content again).
Layer 2: Content optimization (per-page payload)
Per zauberzeug#5833 ("Proposal: Revamped Examples, Search & SEO for nicegui.io"). All depends on Layer 1.
Item
State
Source
Effort
Notes
Per-page meta description content
Not started
zauberzeug#5833 , zauberzeug#5767
Per page
Needs description sourcing strategy (docstring? hand-curated? AI-summarized?)
Open Graph tags
Not started
zauberzeug#5767 (closed), fork seo.py
Per page
Title, description, image, type, locale
Twitter Card tags
Not started
zauberzeug#5767 (closed), fork seo.py
Per page
Summary card
JSON-LD structured data
Not started
zauberzeug#5767 (closed)
Page-class
Organization, SoftwareApplication, BreadcrumbList
Title strategy + 60-char cap
Not started
zauberzeug#5767 (open question)
Per page
Currently no enforcement
Googlebot screenshot: eager first demo
✅ Shipped
zauberzeug#5800 (merged 2026-02-17)
—
First demo no longer lazy
Examples-as-URLs (deep-linkable)
Not started
zauberzeug#5833 problem 1
Large
Each example gets its own /examples/{slug} page with interactive preview
Two-tier search (per-page slim + dedicated search page)
Not started
zauberzeug#5833 problem 2
Large
Server-side fuzzy search experiment was zauberzeug#5827 (self-archived after zauberzeug#5910 made CWV green)
Technological Foundations content page
✅ Shipped
zauberzeug#5835 (merged 2026-04-01)
—
SEO-relevant content addition
Cross-references
Closed-with-context upstream PRs (lore)
SEO optimization for the Documentation website zauberzeug/nicegui#5308 — first SEO attempt, self-abandoned ("back to the dungeon")
Add comprehensive SEO support for documentation website zauberzeug/nicegui#5767 — bundled SEO PR (+502 LoC). Falko BLOCKER Bump cookie and socket.io #1 : "split into separate PRs" → split into Reset _page_exception_handler in App.reset() for test isolation zauberzeug/nicegui#5809 , Add ui.status_code() to set HTTP status codes from page builders zauberzeug/nicegui#5810 (✅), Add lang attribute to <html> tag in page template zauberzeug/nicegui#5811 (🔴), Add comprehensive SEO support for documentation website zauberzeug/nicegui#5813 (closed for rebase)
Enhance Googlebot homepage screenshot: show first demo zauberzeug/nicegui#5793 — Googlebot screenshot enhancement, Falko engaged, superseded
Radical INP improvements zauberzeug/nicegui#5801 — Radical INP improvements, self-killed as "too cursed for mainline"
Add comprehensive SEO support for documentation website zauberzeug/nicegui#5813 — SEO bundle re-submission, self-closed to rebase against Redesign nicegui.io website zauberzeug/nicegui#5910 website redesign
Cut header and tree visual effects for mobile zauberzeug/nicegui#5824 — mobile visual effects cut for INP, became unnecessary after Redesign nicegui.io website zauberzeug/nicegui#5910
Use RapidFuzz for server-side fuzzy search zauberzeug/nicegui#5827 — server-side fuzzy search for INP, self-archived after Redesign nicegui.io website zauberzeug/nicegui#5910 made CWV green
Add i18n support for the NiceGUI website (focused) zauberzeug/nicegui#5848 — i18n PR closed for rebase; tightly coupled to <html lang> (Add lang attribute to <html> tag in page template zauberzeug/nicegui#5811 ) decision
Discussions
Fork branches (this repo)
Maintainer signals to honor
Decision points (open)
Drafted by Claude Code working with @evnchn.
Two-layer SEO work split. Groundwork = plumbing primitives (sitemap, robots, canonical, lang, status codes) — agent-shippable, low-risk, narrowly-scoped Falko-friendly PRs. Content optimization = per-page payload work (meta descriptions, OG, Twitter Cards, JSON-LD, examples-as-URLs, two-tier search) — discussed at zauberzeug#5833, depends on groundwork.
This issue tracks state across both. Ship groundwork before any content.
Layer 1: Groundwork (plumbing primitives)
ui.status_code())seo-pr-clean-reimpl-2026-04-15)<link rel="canonical">website/seo.py)nicegui/nicegui.py) — separate review surface<meta description>mechanism<html lang>attribute<meta robots>per-page noindex/followLive-site state (probed 2026-05-14)
https://nicegui.io/sitemap.xml→ 404https://nicegui.io/robots.txt→ 404 (falls through to SPA HTML)https://nicegui.io/documentation/buttonHEAD has only<meta name="viewport">. No canonical, no description, no OG, no JSON-LD.Recommended ship order
Sitemap:directive; share URL enumeration; ~50 LoC + tests; addresses Falko's Add comprehensive SEO support for documentation website zauberzeug/nicegui#5767 BLOCKER Bump cookie and socket.io #1 verbatim).Layer 2: Content optimization (per-page payload)
Per zauberzeug#5833 ("Proposal: Revamped Examples, Search & SEO for nicegui.io"). All depends on Layer 1.
seo.pyseo.py/examples/{slug}page with interactive previewCross-references
Closed-with-context upstream PRs (lore)
_page_exception_handlerinApp.reset()for test isolation zauberzeug/nicegui#5809, Addui.status_code()to set HTTP status codes from page builders zauberzeug/nicegui#5810 (✅), Addlangattribute to<html>tag in page template zauberzeug/nicegui#5811 (🔴), Add comprehensive SEO support for documentation website zauberzeug/nicegui#5813 (closed for rebase)<html lang>(Addlangattribute to<html>tag in page template zauberzeug/nicegui#5811) decisionDiscussions
element.to_text()protocol (alternative direction: serve text fallbacks)Fork branches (this repo)
seo-pr-clean-reimpl-2026-04-15(Re-implement SEO support for website (ported to website/components/ structure) #137) — full SEO suite ported against post-Redesign nicegui.io website zauberzeug/nicegui#5910 website structure. Source of truth for re-implementations. NOT directly cherry-pickable to upstream main (345 files of unrelated drift); the SEO subset must be carved cleanly onto a fresh branch off upstream/main.Maintainer signals to honor
Decision points (open)
@ui.page+ui.sub_pageregistrations (broader, helps all NiceGUI users — open question in Add comprehensive SEO support for documentation website zauberzeug/nicegui#5813 body, unresolved)?seo.py:extract_description())? Hand-curated? AI-generated?<html lang>strategy: opt-in (only when explicitly set) vs always-on with default? Tied to i18n strategy (Addlangattribute to<html>tag in page template zauberzeug/nicegui#5811, Add i18n support for the NiceGUI website (focused) zauberzeug/nicegui#5848).