Today, when users visit the root / of a DocC archive, they encounter a "soft 404" error page. This creates a poor first impression and doesn't guide visitors toward the actual documentation they're looking for.
For example, visiting https://docs.swift.org/swiftpm/ shows an error, when ideally it should provide a clear landing experience that orients the user and directs them to the documentation modules and tutorials available in that archive.
Why This Matters
The documentation homepage is often the first touchpoint for users discovering a library or framework. A poor experience at this critical moment can:
Frustrate new users and developers
Reduce discoverability of important documentation
Create confusion about whether documentation even exists
Contrast sharply with modern developer documentation sites (like Apple's Developer Docs)
Proposed Solution
Instead of a 404, the archive index at / should display:
A clear heading or hero section introducing the archive
A sidebar showing the top-level navigation structure
Direct links to the primary modules, tutorials, and documentation sections
This can be achieved by leveraging the existing navigator data that DocC already generates, without requiring new output files or changes to existing routes.
This proposal is fully compatible with DocC's merged documentation feature. We're only creating a landing page at /, while leaving the existing /documentation route intact. This means:
Merged archives continue to work as expected
The root landing page complements rather than conflicts with combined documentation
No changes needed to existing merged archive configurations
I think what you're aiming for here is a pitch or ad-hoc review to help mitigate an issue with how the single page app (DocC-Render) works today - and I'm here for it. Today we're actively designing around this gap in the current functionality, along with the gap that a combined documentation page is only synthesized and can't be provided as a "curated experience" (aka rendered from markdown or something)
I think what you're proposing here definitely works as a stop-gap to fix an immediate problem, but the larger issue needs to be addressed as well.
While the docc-archive-as-JSON-bundle-of-data thing worked acceptably, and enabled the potential of native rendering for docs as well as HTML presentation with the single-page app, it brought with it significant challenges in presenting the content (requiring the /documentation in everything) that from a practical perspective has been (and continues to be) a notable challenge.
Thanks for the feedback, @Joseph_heck. I've been exploring a couple of alternatives that might address both the immediate UX problem and the larger architectural concerns.
Alternative 2: "Back to Documentation" Link
Simple, works for both simple and combined archives.
Reuses existing infrastructure (no new code patterns)
Allows curated content via homepage.json
Directly addresses the "curated experience" gap you mentioned
Same model works for simple and combined archives
Trade-offs
Alternative 2 is quicker; Alternative 3 aligns better with the larger direction.
However, with Alternative 3, we need to decide on the actual filename for the data file. This decision affects both the render side and requires changes to swift-docc to emit the file with the right name and content.
Thanks @victorpuga - to be clear, I'm not the owner/maintainer of DocC, more a very frequent user/consumer of its features as a means to present documentation, tutorials, and guides. The alternatives and suggestions you presented after were more limited in scope than what I was advocating for thinking - smaller and more focused, and maybe what you want to pitch or suggest - but not really what I was suggesting.
I think if we stay within the Javascript/single-page-app mode, there's a broader question that we need to address of how to provide a curated/edited view of a top-level page that directs and presents readers from a root URL or the root URL + /documentation.
While this is the status quo today, I'm not personally convinced this is working to the project and our reader's benefits, and that maybe we need to spend some time focused on a display that doesn't require a Javascript intermediary. The moving parts are all in the code base to allow us to render a fully static HTML site from a DocC archive collection. I think there's a lot of benefit in having the intermediary DocC Archive that we can merge, or display individually - and now that we're more actively merging more of these, it's become clear that the navigation between individual archive sets combined into a collection - while functional - could use some more attention in terms of navigating between the collections, how that's visually presented, and how we provide or present a top-level visual of the collection that can be explicitly designed, not just synthesized.
In my view, the quick-nav left panel - which in a single archive is pretty effective - doesn't hold up as well when you have multiple collections in a merged archive that you're displaying. As an example, when you're viewing: Package Manager Docs, it's not at all clear that the collection includes the sibling PackagePlugin API docs and PackageDescription API docs - although you can see both from that synthesized page at the top (https://docs.swift.org/swiftpm/documentation/)
The collections show a disclosure triangle in the combined left nav so you can expand/collapse each collection, but that's it. On top of that, the left nav setup is (to me) visually larger and more prominent in the design and layout than I think it should be - detracting from the core content that's being made available - the docs themselves. I'd personally prefer a far more compact visual representation - keeping the searching and navigation capability, and perhaps expanding to even a breadcrumb concept so that you have a better representation of where you are within a deeper or broader collection of docs.
I'm not at all suggesting that your proposal needs adjustment for a pitch, more airing the pain points I'm seeing and broader conversation that I think the project should explore.
Thanks for the further clarification, @Joseph_Heck. You're articulating a much broader architectural concern than what I was addressing and I think you're right to push on this.
The merged archive navigation problem you're describing is real. When you have multiple collections combined, the left sidebar doesn't effectively communicate the relationships between them or help readers understand the scope of what's available.
Potential approach:
Scoped Navigation
Only show the current archive's pages in the left nav, with a selector or breadcrumb to switch between collections. This keeps the visual hierarchy cleaner and makes the structure of each collection immediately apparent.
As part of this, we could include a link in the "Documentation" text at the top of the sidebar that takes readers to the homepage, where they'd be able to see the combined archive's root and understand the full scope of what's available.
I personally want to address the homepage issue, but not as a quick patch. I want to do it in a way that's part of the larger solution. I feel strongly about adding a home route that renders DocumentationTopic.vue , mapping / to /data/homepage.json, because it treats the homepage as a first-class citizen in the data model and creates a foundation that can evolve with the broader architectural improvements.
Whether that's through a curated landing page, improved navigation structure, or rethinking how collections present themselves at the root, it should all work together.
I'd love to hear @ronnqvist and @marcus_ortiz's thoughts on this. What does the broader vision look like for merged archive presentation, and how should the homepage/root experience fit into that?
My feelings is that adding another page is a stop-gap solution to an immediate problem and that it would ultimately be phased out when addressing the core issues.
I still feel that it's perfectly fine to do it as a stop gap solution and believe that the timescale for it to be phased out could possibly be measured in years.
If we go back to the original problem statement:
visiting https://docs.swift.org/swiftpm/ shows an error, when ideally it should provide a clear landing experience that orients the user and directs them to the documentation modules and tutorials available in that archive.
I feel that the root cause of that issue is the need for a leading "documentation" path component which—as far as I know—is required by Docc Render to know what type of documentation page is requested (if it's a tutorial or not) so that it can load the right resources and put together the page. If that path component wasn't required then the https://docs.swift.org/swiftpm could be the page that's on https://docs.swift.org/swiftpm/documentation today and the documentation for its modules (for example "PackageDescription") could be https://docs.swift.org/swiftpm/packagedescription.
Additionally, if we look ahead—at what's intended but not yet implemented for combined documentation archives—when it becomes possible to pass a catalog to the merge command, then the documentation that's currently at https://docs.swift.org/swiftpm/documentation/packagemanagerdocscould become the landing page for the combined archive (which without the "documentation" component would be https://docs.swift.org/swiftpm) with the "PackageDescription" and "PackagePlugin" documentation having their own sub paths (https://docs.swift.org/swiftpm/packagedescription and https://docs.swift.org/swiftpm/packageplugin).
I don't know how much work it is to change DocC Render to not require a leading "documentation" component but this has been an issue for 5 years now.
My personal vision is that DocC would transition to fully static HTML files as the default output format, that deploying to a different location would be as simple as moving the files there, and that renaming a path component would be as simple as renaming a file/directory.
I made some progress on this last year and DocC can now (as of the 6.3 release) emit a static HTML representation of the page's content for environments that don't run JavaScript. As mentioned in the original pitch, one natural next step would be to add a small amount of CSS to improve the reading experience; with the goal of looking exactly the same as the DocC Render single-page-app version. I've been finding a little bit of time to explore this but I'm still not ready to even open an early work-in-progress (draft) PR or to pitch the idea with more details here in the forums.
Thanks for sharing that vision, @ronnqvist. I totally get the appeal of moving toward static HTML output. It's elegant, portable, and solves a lot of the fundamental architectural issues you're describing.
I do have a few questions as I think through this though.
What would you suggest for the immediate fix? The /swiftpm/ → 404 problem affects users today, and even if the long-term solution is HTML-based, we need something in the meantime.
I already have forks of swift-docc and swift-docc-render that handle the new homepage with homepage.json. The implementation is nearly done, but I'm wondering if it's worth maintaining and upstreaming if it's ultimately going to be removed when the static HTML approach ships. What's your take on that?
I'm also genuinely eager to help move the static HTML rendering feature forward. What are the next steps, and where could I contribute effectively? Is there work on the styling layer you mentioned, or would it be more helpful to focus on a specific part of the pipeline?
I’m not sure where I stand on the immediate issue. If either solution is significantly more isolated, and therefore easier to remove in the future, then that might be worth considering. If they’re about the same then I don’t know.
I also have no grasp of how hard it would be to remove the requirement of the “documentation” path component in DocC Render. I’m expecting it to be significantly less work than creating an entire static HTML solution (even thought progress has already started) but I’m not familiar enough with the DocC Render code to know how much less work.
I don’t expect maintaining it would be too much trouble. It’s more how you feel about the remaining work.
It might be relevant to consider roughly how many releases we think that we’ll have a / page. For example; if removing the “documentation” component would be sufficient to also remove the / page and if that’s something that could feasibly be accomplished for the 6.5 release, then it would be good to know if the / is close enough to complete that it could make it into the 6.4 release (which has already branched and was converging for a few weeks before that) or if it’s looking more likely that it would be added in the 6.5 release (as well).
There is both styling work and some additional components (mainly page layout directives) that DocC would need to output static HTML for.
To avoid repeating work, I think the next clear action is on me to minimally clean up my work-in-progress exploration to the point where others can collaborate on the styling and static HTML output (but not necessarily to the point where it’s “ready” for general experimentation usage. Right now is a rather busy time of the year but I’ll set myself a goal to have that minimal clean-up pushed within a month.
Your response actually clarifies things for me. I'm leaning toward just adding a "back to documentation" link to the 404 page. It's considerably less effort and only touches swift-docc-render, which means it'll be much easier to remove when the path component requirement is eventually lifted.
That said, I'd still like to document the implementation plan for a customizable / page across both repositories, in case there's interest in moving forward with it or if it helps inform the broader architecture work.
Implementation of a new @HomePage directive that enables custom homepage content in Swift-DocC.
This feature allows developers to write a custom homepage article (at /well-known/homepage) that will be prioritized over the auto-synthesized homepage, while maintaining backward compatibility with synthesized homepages.
The feature includes improvements to both authored and synthesized homepages:
Custom homepages are preserved during archive merging operations
Authored homepages are rendered separately and output only as JSON (no HTML)
Synthesized homepages use a detailedGrid style with separate Modules and Tutorials sections
The homepage JSON is always available at data/well-known/homepage.json
Added isCustomHomepageContent property to flag authored homepages
Properly encoded/decoded in Codable conformance for archive preservation
Synthesized Homepage Generation
Modules Section
Collects root pages from context.linkResolver.localResolver.rootPages()
Creates TopicRenderReference objects using renderer for proper abstracts
Organized under "Modules" section with #Modules anchor
Tutorials Section
Collects tutorial references from context.tutorialTableOfContentsReferences
Rendered with full abstracts and details
Organized under "Tutorials" section with #Tutorials anchor
Styling
Uses .detailedGrid style for large card display with abstracts
Only creates sections if content exists (no empty sections)
Set generated: true to indicate auto-created sections
Output Handling
JSON-Only Output
Homepage always written to data/well-known/homepage.json
No HTML file generated (prevents /well-known/homepage/index.html)
Enables clean integration with external tools and services
Processing Order
Regular pages rendered (homepage filtered out)
Authored homepage rendered separately if present
Else synthesized homepage created with modules and tutorials
Homepage consumed via consume(homepage:) for JSON output
Architecture Overview
Data Flow
Authored Homepage Article (/well-known/homepage)
↓
Rendered separately (filtered from normal page rendering)
↓
Marked with isCustomHomepageContent = true
↓
consume(homepage:) → data/well-known/homepage.json (JSON only, no HTML)
OR
Synthesized Homepage (fallback if no authored homepage)
↓
Collect root pages (documentation modules)
↓
Collect tutorial references
↓
Create separate "Modules" and "Tutorials" sections (detailedGrid style)
↓
consume(homepage:) → data/well-known/homepage.json (JSON only)
Merge Process with Custom Homepages
Input Archives
↓
Check each archive for existing homepage.json
↓
If custom homepage found: preserve all content fields
↓
Warn if multiple custom homepages exist
↓
If no custom homepage: synthesize new one
↓
Output merged archive with homepage at data/well-known/homepage.json
Key Features
Authored Homepages
Write custom homepage content using @HomePage directive
Full control over layout, abstracts, sections, and styling
Preserved during archive merging operations
Synthesized Homepages (Fallback)
Auto-generated if no custom homepage provided
Displays all documentation modules and tutorials
Uses detailedGrid style with abstracts
Organized into separate "Modules" and "Tutorials" sections
JSON Output Only
Homepage always available at data/well-known/homepage.json
No HTML page generated (no /well-known/homepage/index.html)
Clean integration with external tools and services
Archive Merging Support
Custom homepages are fully preserved during merges
All content variants and styling maintained
Clear warnings if multiple custom homepages detected
Integration Points
API Changes
New Protocol Method:ConvertOutputConsumer.consume(homepage:)
New Metadata Flag:RenderMetadata.isCustomHomepageContent
File Output
Location:data/well-known/homepage.json
Format: JSON-encoded RenderNode
Timing: Emitted after all page rendering completes
Configuration
Uses existing context.hasHomePage detection
Compatible with current feature flag system
Testing Recommendations
Authored Homepage Tests
Create a package with @HomePage directive
Verify authored homepage appears at data/well-known/homepage.json
Verify no HTML file is generated
Verify custom abstracts and content are preserved
Synthesized Homepage Tests
Create packages without custom homepages
Verify synthesized homepage includes all modules and tutorials
Verify sections are properly separated with anchors
Verify detailedGrid styling is applied
Merge Tests
Merge archives with custom homepages
Verify all content variants are preserved
Verify warning appears when multiple custom homepages exist
Verify synthesized homepage created when merging archives without custom ones
Edge Cases
Empty modules/tutorials (no sections generated)
Mixed custom and synthesized content in merged archives
Platform-specific content variants in homepages
Backward Compatibility
Fully backward compatible:
Existing packages without @HomePage directive get synthesized homepages
Homepage Feature with Locale Support — Implementation Overview
Summary
This feature implements a homepage route (/) that loads dedicated homepage content (/data/well-known/homepage.json), with full support for locale-prefixed routes (/ar/, /en-US/, etc.). The homepage reuses the existing DocumentationTopic view and integrates seamlessly with the locale routing system.
Implementation Details
Routing & Navigation
Homepage route: Added a new root route (/) that renders the DocumentationTopic.vue component
Route naming: Route is named 'documentation-topic-home' (contains 'documentation-topic' substring) so existing scroll behavior checks work without modification
Locale support: Homepage is accessible at all locale-prefixed paths (/ar/, /en-US/, /zh-CN/, /ja-JP/, /ko-KR/) while fetching the same homepage data
Locale routing constraints: The :locale route parameter is constrained to only match valid locale slugs to prevent paths like /documentation from being incorrectly matched as /:locale?/documentation
Home navigation: The "Documentation" text in the sidebar now links to the homepage (or technology page if available), allowing users to navigate home from anywhere in the docs
Data & Path Management
Data location: Homepage data is served from /data/well-known/homepage.json
Path mapping: The createDataPath() utility maps root path (/) to /data/well-known/homepage.json
Locale-aware fetching:fetchDataForRouteEnter() detects home routes by name and always fetches from the homepage data location, regardless of locale prefix
Locale prefix preservation: When navigating home from a locale-prefixed page, the locale prefix is included in the destination URL (e.g., /ar/documentation/foo → /ar/)
Navigation & UI
Homepage hierarchy: The homepage displays the full documentation index hierarchy, including all top-level modules when multiple exist at the same level
Filtered sidebar: Well-known paths (e.g., /well-known/homepage) are filtered from the sidebar navigation to keep the interface focused
Technology link: The technology link is hidden when visiting the root path (/)
Root link behavior: The sidebar's "Documentation" title is now always clickable:
Links to the technology page if one exists in the hierarchy
Links to / (or /:locale/ for locale-prefixed routes) as fallback
Testing
Comprehensive test coverage for locale-prefixed home route data fetching
Tests verify that all locale variants fetch homepage data correctly
Tests ensure non-home locale routes continue to fetch locale-specific data (e.g., /ar/documentation/... fetches /data/ar/documentation/....json)
Route pattern tests updated to reflect constrained locale matching
Component tests updated to verify "Documentation" title renders as a clickable link
Key Design Decisions
Reuse DocumentationTopic view: Homepage uses the same view component as documentation pages, minimizing code duplication and leveraging existing scroll behavior.
Well-known location: Homepage data lives at /data/well-known/homepage.json.
Locale-aware routing: Homepage respects locale prefixes—visiting /ar/ fetches the same homepage content but maintains the locale context for subsequent navigation.
Constrained locale pattern: Locale parameter is restricted to valid slugs to prevent routing ambiguity and 404 errors.
Filtered sidebar: Well-known paths are excluded from sidebar navigation to keep the interface clean and focused.
After laying out the actual implementation plan, I'm wondering if your take changes at all. Does the 404 link still feel like the right call, or does the feature seem worth pursuing?
Hi, @marcus_ortiz. I'd love your input on this direction. After laying out the fuller implementation plan for Alternative 3, I've decided to step back:
I'm leaning toward just adding a "back to documentation" link to the 404 page. It's considerably less effort and only touches swift-docc-render, which means it'll be much easier to remove when the path component requirement is eventually lifted.
The reasoning is that David's vision for static HTML output and removing the /documentation path component is the real architectural fix. Alternative 3, while architecturally sound, would ultimately be scaffolding we'd tear down.
But I'm curious whether you see trade-offs here I might be missing. From your work on the renderer, does the "back to documentation" link feel like a sufficient stop-gap, or does the data-driven approach feel necessary for something else, maybe for merged archive UX or navigation patterns you're thinking about?
I want to make sure we're not choosing the simpler path at the cost of something more important down the line.
I think the "Back to Documentation" addition to the 404 page could be a good enhancement, even if it's just an intermediate solution for now.
I would be a little hesitant to add a root / route for the application before a more complete solution is ready though, since the renderer output from DocC is meant to be something that could be easily dropped into an existing website that may or may not already have its own root webpage (and other pages).
Currently all routes are explicitly prefixed with either /documentation or /tutorials to more easily avoid those conflicts, although I can definitely see the frustration and confusion from the root route being a 404 for common scenarios where a developer hosts their docs on something like GitHub Pages.
I've got a PR ready. My approach: redirect the 404 to the first top-level module in the index. Works for both single and combined archives, and respects the route-prefixing design.
Would appreciate your feedback on the implementation.