Overview
Documentation Collections allow you to combine multiple independent .docsproj projects into a single unified documentation portal. This feature is perfect for:
- Microservices: Create a single portal for all services in your architecture
- Multi-Product Suites: Unify documentation across separate open-source products
- Modular Systems: Combine documentation from independently maintained modules
Collections work by copying documentation files from referenced projects and combining their navigation structures into a single cohesive experience.
Collections are currently supported for Mintlify documentation only. Support for other documentation formats may be added in future releases.
How Collections Work
The collection .docsproj is a normal, first-class documentation project that:
- Has its own assemblies to document (or none at all)
- Has its own conceptual content and guides
- Has its own branding and theme configuration
- Generates its own documentation using the standard pipeline
After the collection completes its normal documentation generation, DotNetDocs:
- Copies markdown files from referenced projects into the collection’s folder structure
- Loads each referenced project’s
docs.json navigation file
- Applies URL prefixes to navigation paths
- Combines navigation into the collection’s Tabs or Products arrays
- Saves the final
docs.json once with everything integrated
No complex merging logic or priorities neededjust copy files and combine navigation. This “Easy AF” design keeps collections simple and predictable.
Collection Root Content
Most documentation collections have their own landing pages, guides, and conceptual content that serve as the “home” for the unified portal. This root content provides:
- Landing pages that introduce the overall platform or product suite
- Getting started guides that span multiple referenced projects
- Architecture documentation that explains how components fit together
- Shared resources like images and snippets used across the collection
Controlling Root Navigation with MintlifyTemplate
The <Navigation> element inside <MintlifyTemplate> controls how the collection’s own content appears alongside referenced documentation:
Configures how the collection’s root content integrates with referenced documentation.Attributes:| Attribute | Description | Values |
|---|
Mode | How navigation groups are organized | Unified (single tree) or Separated (per-assembly) |
Type | Where root content appears in navigation | Pages (main nav), Tabs (as a tab), or Products (as a product) |
Name | Display name when using Tabs or Products | Custom string (e.g., “Platform Overview”) |
Navigation Type Examples
Pages (Default)
Tabs
Products
Root content appears in the main navigation alongside groups. Referenced docs appear as tabs.<MintlifyTemplate>
<Name>My Platform</Name>
<Navigation Mode="Unified" Type="Pages">
<Pages>
<Groups>
<Group Name="Getting Started" Icon="rocket">
<Pages>index;quickstart;architecture</Pages>
</Group>
</Groups>
</Pages>
</Navigation>
</MintlifyTemplate>
Root content appears as its own tab, giving it equal prominence with referenced documentation.<MintlifyTemplate>
<Name>My Platform</Name>
<Navigation Mode="Unified" Type="Tabs" Name="Platform Overview">
<Pages>
<Groups>
<Group Name="Getting Started" Icon="rocket">
<Pages>index;quickstart</Pages>
</Group>
<Group Name="Architecture" Icon="sitemap">
<Pages>architecture;deployment</Pages>
</Group>
</Groups>
</Pages>
</Navigation>
</MintlifyTemplate>
This creates a “Platform Overview” tab alongside tabs for each DocumentationReference. For multi-product portals where the collection itself is a product.<MintlifyTemplate>
<Name>EasyAF Suite</Name>
<Navigation Mode="Unified" Type="Products" Name="Suite Overview">
<Pages>
<Groups>
<Group Name="Introduction" Icon="house">
<Pages>index;overview</Pages>
</Group>
</Groups>
</Pages>
</Navigation>
</MintlifyTemplate>
When Type is Tabs or Products, the Name attribute determines the display label. If omitted, the collection’s <Name> value is used.
Configuration
Using DocumentationReference
Add <DocumentationReference> items to your collection’s .docsproj file, similar to how you use <ProjectReference>:
<Project Sdk="DotNetDocs.Sdk/1.2.0">
<PropertyGroup>
<DocumentationType>Mintlify</DocumentationType>
<GenerateDocumentation>true</GenerateDocumentation>
</PropertyGroup>
<ItemGroup>
<DocumentationReference Include="../ServiceA/ServiceA.docsproj"
DestinationPath="services/service-a"
IntegrationType="Tabs" />
<DocumentationReference Include="../ServiceB/ServiceB.docsproj"
DestinationPath="services/service-b"
IntegrationType="Tabs" />
<DocumentationReference Include="../ServiceC/ServiceC.docsproj"
DestinationPath="services/service-c"
IntegrationType="Tabs" />
</ItemGroup>
</Project>
Required Attributes
Relative or absolute path to the referenced .docsproj file.<DocumentationReference Include="../MyService/MyService.docsproj" ... />
URL path where the referenced documentation will be accessible. This becomes the URL prefix for all documentation from this reference.DestinationPath="services/user-service"
Files from the referenced project appear at URLs like:
services/user-service/api-reference/...
services/user-service/guides/...
Optional Attributes
How the reference’s navigation is integrated into the collection. Supported values:
Tabs - Adds as a top-level tab in Mintlify navigation
Products - Adds as a product in Mintlify’s multi-product navigation
Custom display name for this documentation in navigation. If not specified, the project file name (without extension) will be used.Name="User Management Service"
This is especially useful when project file names aren’t user-friendly (e.g., ServiceA.docsproj can display as “Authentication Service”).
Integration Types
Mintlify supports two ways to integrate referenced documentation into your navigation structure.
Tabs Integration
Tabs appear as top-level navigation items in your documentation site. This is the default integration type.
<ItemGroup>
<DocumentationReference Include="../UserService/UserService.docsproj"
DestinationPath="services/users"
IntegrationType="Tabs"
Name="User Management" />
<DocumentationReference Include="../OrderService/OrderService.docsproj"
DestinationPath="services/orders"
IntegrationType="Tabs"
Name="Order Processing" />
</ItemGroup>
Result in docs.json:
{
"name": "Microservices Platform",
"tabs": [
{
"tab": "User Management",
"href": "services/users",
"pages": [
"services/users/introduction",
"services/users/api-reference/overview",
...
]
},
{
"tab": "Order Processing",
"href": "services/orders",
"pages": [
"services/orders/introduction",
"services/orders/api-reference/overview",
...
]
}
]
}
If you omit the Name attribute, the project file name is used (e.g., “UserService” and “OrderService”).
Products Integration
Products are designed for multi-product documentation portals where each product has its own complete documentation set.
<ItemGroup>
<DocumentationReference Include="../Core/Core.docsproj"
DestinationPath="core"
IntegrationType="Products"
Name="EasyAF Core" />
<DocumentationReference Include="../Http/Http.docsproj"
DestinationPath="http"
IntegrationType="Products"
Name="HTTP Client" />
<DocumentationReference Include="../Validation/Validation.docsproj"
DestinationPath="validation"
IntegrationType="Products"
Name="Validation Framework" />
</ItemGroup>
Result in docs.json:
{
"name": "EasyAF Product Suite",
"navigation": {
"products": [
{
"product": "EasyAF Core",
"href": "core",
"pages": [...],
"groups": [...]
},
{
"product": "HTTP Client",
"href": "http",
"pages": [...],
"groups": [...]
},
{
"product": "Validation Framework",
"href": "validation",
"pages": [...],
"groups": [...]
}
]
}
}
Complete Examples
Example 1: Microservices Portal
Create a unified documentation portal for a microservices architecture:
File: docs/MicroservicesPlatform.docsproj
<Project Sdk="DotNetDocs.Sdk/1.2.0">
<PropertyGroup>
<!-- Collection configuration -->
<DocumentationType>Mintlify</DocumentationType>
<GenerateDocumentation>false</GenerateDocumentation>
<!-- Collection branding with Navigation configuration -->
<MintlifyTemplate>
<Name>Microservices Platform</Name>
<Theme>quill</Theme>
<Colors>
<Primary>#0066CC</Primary>
</Colors>
<!-- Root content appears as "Platform" tab alongside service tabs -->
<Navigation Mode="Unified" Type="Tabs" Name="Platform">
<Pages>
<Groups>
<Group Name="Overview" Icon="house">
<Pages>index;architecture</Pages>
</Group>
<Group Name="Getting Started" Icon="rocket">
<Pages>quickstart;deployment</Pages>
</Group>
</Groups>
</Pages>
</Navigation>
</MintlifyTemplate>
</PropertyGroup>
<ItemGroup>
<!-- Reference individual service documentation with custom names -->
<DocumentationReference Include="../services/UserService/UserService.docsproj"
DestinationPath="services/users"
IntegrationType="Tabs"
Name="User Management" />
<DocumentationReference Include="../services/OrderService/OrderService.docsproj"
DestinationPath="services/orders"
IntegrationType="Tabs"
Name="Order Processing" />
<DocumentationReference Include="../services/PaymentService/PaymentService.docsproj"
DestinationPath="services/payments"
IntegrationType="Tabs"
Name="Payments" />
<DocumentationReference Include="../services/NotificationService/NotificationService.docsproj"
DestinationPath="services/notifications"
IntegrationType="Tabs"
Name="Notifications" />
</ItemGroup>
</Project>
Resulting Structure:
docs/
introduction.md # Collection's own introduction
architecture.md # Collection's own architecture guide
services/
users/ # UserService documentation
introduction.md
api-reference/
orders/ # OrderService documentation
introduction.md
api-reference/
payments/ # PaymentService documentation
notifications/ # NotificationService documentation
docs.json # Combined navigation
Example 2: Multi-Product Suite
Create a unified portal for related open-source products:
File: docs/EasyAF.Portal.docsproj
<Project Sdk="DotNetDocs.Sdk/1.2.0">
<PropertyGroup>
<DocumentationType>Mintlify</DocumentationType>
<MintlifyTemplate>
<Name>EasyAF Product Suite</Name>
<Theme>maple</Theme>
<!-- Root content appears as "Suite Overview" product -->
<Navigation Mode="Unified" Type="Products" Name="Suite Overview">
<Pages>
<Groups>
<Group Name="Welcome" Icon="hand-wave">
<Pages>index;getting-started</Pages>
</Group>
<Group Name="Concepts" Icon="lightbulb">
<Pages>architecture;best-practices</Pages>
</Group>
</Groups>
</Pages>
</Navigation>
</MintlifyTemplate>
</PropertyGroup>
<ItemGroup>
<!-- Reference separate product repositories with custom names -->
<DocumentationReference Include="../../EasyAF.Core/docs/EasyAF.Core.docsproj"
DestinationPath="core"
IntegrationType="Products"
Name="Core Framework" />
<DocumentationReference Include="../../EasyAF.Http/docs/EasyAF.Http.docsproj"
DestinationPath="http"
IntegrationType="Products"
Name="HTTP Client" />
<DocumentationReference Include="../../EasyAF.Validation/docs/EasyAF.Validation.docsproj"
DestinationPath="validation"
IntegrationType="Products"
Name="Validation" />
</ItemGroup>
</Project>
Example 3: Hybrid Collection
A collection that documents its own assemblies AND references external documentation:
<Project Sdk="DotNetDocs.Sdk/1.2.0">
<PropertyGroup>
<!-- This collection also generates its own API docs -->
<DocumentationType>Mintlify</DocumentationType>
<GenerateDocumentation>true</GenerateDocumentation>
<NamespaceMode>Folder</NamespaceMode>
<MintlifyTemplate>
<Name>My Platform</Name>
<!-- Root content (including generated API docs) appears as "Core" tab -->
<Navigation Mode="Unified" Type="Tabs" Name="Core">
<Pages>
<Groups>
<Group Name="Overview" Icon="house">
<Pages>index;quickstart</Pages>
</Group>
</Groups>
</Pages>
</Navigation>
</MintlifyTemplate>
</PropertyGroup>
<ItemGroup>
<!-- Reference external plugin documentation -->
<DocumentationReference Include="../Plugins/AI/PluginAI.docsproj"
DestinationPath="plugins/ai"
IntegrationType="Tabs"
Name="AI Plugin" />
<DocumentationReference Include="../Plugins/GitHub/PluginGitHub.docsproj"
DestinationPath="plugins/github"
IntegrationType="Tabs"
Name="GitHub Plugin" />
</ItemGroup>
</Project>
Validation and Safety
DotNetDocs validates references during the build process to ensure everything works correctly.
Build-Time Validation
The DocumentationReferenceResolverTask checks:
- Project Exists: The referenced
.docsproj file must exist
- Documentation Type Match: Referenced projects must use the same
DocumentationType as the collection
- Outputs Exist: The referenced project must have generated documentation
- Navigation File Exists: For Mintlify projects,
docs.json must exist
Type Mismatch Warnings
If you try to reference a project with a different DocumentationType, you’ll see a warning:
Skipping documentation reference 'ServiceA.docsproj' because it uses 'DocFX' format.
Only 'Mintlify' documentation can be combined with Mintlify collections.
Cross-format documentation combination is not currently supported.
The reference will be skipped, but the build continues successfully.
Build the referenced projects before building the collection. The collection copies existing documentation outputsit doesn’t build referenced projects automatically.
URL Prefixes and Navigation
DotNetDocs automatically applies URL prefixes to all navigation paths when combining referenced documentation.
How Prefixes Work
Given this reference:
<DocumentationReference Include="../ServiceA/ServiceA.docsproj"
DestinationPath="services/service-a" />
Original navigation in ServiceA/docs.json:
{
"pages": [
"introduction",
"api-reference/overview",
{
"group": "API Reference",
"pages": [
"api-reference/classes/user",
"api-reference/classes/order"
]
}
]
}
After prefix application in collection:
{
"pages": [
"services/service-a/introduction",
"services/service-a/api-reference/overview",
{
"group": "API Reference",
"pages": [
"services/service-a/api-reference/classes/user",
"services/service-a/api-reference/classes/order"
]
}
]
}
Deep Prefix Application
URL prefixes are applied recursively at all nesting levels:
- String pages: Direct path strings
- Groups: Pages inside navigation groups
- Tabs: Pages inside nested tabs
- Dropdowns: Pages inside dropdown menus
This ensures all links work correctly regardless of navigation structure complexity.
File Copying Behavior
DotNetDocs copies documentation files based on the referenced project’s DocumentationType.
Mintlify File Patterns
For Mintlify references, these patterns are copied:
*.md, *.mdx, *.mdz - All markdown files
images/**/* - All images
logo/**/* - Logo assets
Conflict Resolution
If the same file exists in both the collection and a reference:
Collection wins: The collection’s file is kept, and the referenced file is skipped. This allows the collection to override content from references.
This is intentionalcollections can provide their own versions of files to customize the experience.
Build Process
Understanding the build order helps troubleshoot issues:
Build Referenced Projects
Build each referenced .docsproj first. This generates their documentation outputs.dotnet build ServiceA/ServiceA.docsproj --configuration Release
dotnet build ServiceB/ServiceB.docsproj --configuration Release
Build Collection
Build the collection .docsproj. It validates references, copies files, and combines navigation.dotnet build MicroservicesPlatform.docsproj --configuration Release
Validation
The SDK validates that:
- Referenced projects exist
- Documentation types match
- Documentation outputs are available
Collection Generation
The collection generates its own documentation (if GenerateDocumentation=true).
File Copying
Files from referenced projects are copied to DestinationPath locations.
Navigation Combining
The MintlifyRenderer combines navigation from all references into the collection’s docs.json.
Save
The final docs.json is saved once with everything combined.
Troubleshooting
”Referenced project not found”
Problem: The .docsproj path is incorrect or the file doesn’t exist.
Solution: Verify the path in your DocumentationReference is correct. Use relative paths from the collection .docsproj location.
”Documentation root does not exist”
Problem: The referenced project hasn’t been built yet, so its documentation outputs don’t exist.
Solution: Build referenced projects before building the collection:
# Build dependencies first
dotnet build ServiceA/ServiceA.docsproj
dotnet build ServiceB/ServiceB.docsproj
# Then build collection
dotnet build MicroservicesPlatform.docsproj
Problem: The referenced project uses a different DocumentationType than the collection.
Solution: Ensure all referenced projects use the same documentation type. Collections can only combine documentation of the same format (currently Mintlify only).
Missing navigation or broken links
Problem: URL prefixes aren’t being applied correctly, or files aren’t being copied.
Solution:
- Check that
DestinationPath matches where files are actually copied
- Verify the referenced project’s
docs.json exists and is valid JSON
- Rebuild both the references and collection from scratch
Best Practices
<CardGroup cols={2}>
<Card title="Build Order Matters" icon="arrows-turn-right">
Always build referenced projects before building the collection. Use solution build order or explicit build scripts.
</Card>
<Card title="Use Consistent Paths" icon="folder-tree">
Organize `DestinationPath` consistently (e.g., all services under `services/`, all plugins under `plugins/`).
</Card>
<Card title="Keep References Simple" icon="minimize">
Avoid circular references or overly complex reference chains. Keep your dependency graph flat and straightforward.
</Card>
<Card title="Version Together" icon="code-branch">
When releasing, ensure referenced projects and collections use compatible versions of DotNetDocs.Sdk.
</Card>
<Card title="Test Independently" icon="vial">
Each referenced project should work standalone before being added to a collection. Test them independently first.
</Card>
<Card title="Document the Collection" icon="book">
Add a `README.md` or introduction page explaining the collection structure and how projects relate to each other.
</Card>
</CardGroup>
Limitations
Current limitations of Documentation Collections:
-
Mintlify Only: Collections currently only work with Mintlify documentation. Support for DocFX, MkDocs, etc. may be added later.
-
No Cross-Format: You cannot combine Mintlify and DocFX documentation in the same collection. All references must use the same format.
-
Build-Time Only: Collections are resolved at build time. You cannot dynamically add references at runtime.
-
No Transitive References: If ProjectA references ProjectB, and you reference ProjectA in your collection, you don’t automatically get ProjectB. Each reference must be explicit.
-
Single SDK Version: All referenced projects should use the same (or compatible) versions of DotNetDocs.Sdk to ensure consistent behavior.
Technical Details
For those interested in how collections work under the hood:
<AccordionGroup>
<Accordion title="MSBuild Integration" icon="gear">
The `DocumentationReferenceResolverTask` runs during build to:
1. Load each referenced `.docsproj` file
2. Extract `DocumentationRoot` and `DocumentationType` properties
3. Validate that documentation outputs exist
4. Populate `ResolvedDocumentationReference` items with metadata
These resolved items are passed to `GenerateDocumentationTask`, which populates `ProjectContext.DocumentationReferences`.
</Accordion>
<Accordion title="File Copying" icon="copy">
The `DocumentationManager.CopyReferencedDocumentationAsync()` method:
1. Iterates through each `DocumentationReference`
2. Determines file patterns based on `DocumentationType`
3. Copies files from `DocumentationRoot` to `DestinationPath`
4. Skips files that already exist in the collection (collection wins)
Copying happens **after** rendering completes.
</Accordion>
<Accordion title="Navigation Combining" icon="diagram-project">
The `MintlifyRenderer.CombineReferencedNavigation()` method:
1. Loads each reference's `docs.json` using `DocsJsonManager`
2. Applies URL prefix using `ApplyUrlPrefix(DestinationPath)`
3. Adds navigation to `Tabs` or `Products` array based on `IntegrationType`
4. Saves the combined `docs.json` once at the end
This happens **inside** `RenderAsync()` before saving, not as a separate step.
</Accordion>
<Accordion title="URL Prefix Application" icon="link">
The `DocsJsonManager.ApplyUrlPrefix()` method recursively processes:
- **String pages**: Prepends the prefix
- **GroupConfig.Pages**: Recursively processes nested pages
- **TabConfig.Pages**: Recursively processes nested pages
- **DropdownConfig.Pages**: Recursively processes nested pages
This ensures URL prefixes are applied at **all** nesting levels for correct navigation.
</Accordion>
</AccordionGroup>
Next Steps
<CardGroup cols={2}>
<Card title=".docsproj Reference" icon="gear-complex-code" href="/guides/docsproj">
Learn about all available MSBuild properties for documentation projects
</Card>
<Card title="Deployment" icon="conveyor-belt-boxes" href="/guides/deployment">
Deploy your collection to Mintlify, GitHub Pages, or other hosting platforms
</Card>
<Card title="Pipeline Overview" icon="faucet-drip" href="/guides/pipeline">
Understand how the documentation pipeline processes assemblies and content
</Card>
<Card title="Mintlify Provider" icon="sparkles" href="/providers/mintlify">
Deep dive into Mintlify-specific features and configuration
</Card>
</CardGroup>