Navigation Helpers
Outline
- Retrieval Core: IContentLoader serves as the primary read-only interface, leveraging built-in object caching for efficiency
- Security First: FilterForVisitor ensures content visibility respects publication status, access rights, and active date ranges
- Pattern Choice: ViewComponents provide the standard architecture for recursive menu logic and breadcrumb generation
- Performance: Fragment caching in Razor prevents redundant execution of navigation logic on every request
Navigation is the functional manifestation of a website's Information Architecture. In Optimizely CMS 12, menu generation is a decoupled process where technical teams leverage the Content API to retrieve, filter, and render hierarchical data. For high-performance PaaS environments, implementing efficient traversal logic and robust caching strategies is mandatory.
1. The Retrieval API: IContentLoader
For read-only navigation operations, IContentLoader is the primary interface. Unlike IContentRepository, which provides CRUD capabilities, IContentLoader is optimized for retrieval and automatically participates in the Optimizely object cache.
Key Retrieval Methods
Select a method to expand and read the details.
GetChildren<T>(ContentReference) ▼
Retrieves the immediate children of a specific node. This is the bedrock of level-by-level menu generation and the most frequently called method in navigation logic. Always filter the result through FilterForVisitor before rendering.
GetAncestors(ContentReference) ▼
Retrieves the path from the current node back to the root. Primarily used for breadcrumb generation - reverse the result to get root-to-current ordering before rendering.
GetDescendants(ContentReference) ▼
Retrieves the entire subtree below a node. While powerful for sitemap generation, use this sparingly in navigation - in large content trees it can cause excessive database load and memory pressure. Prefer GetChildren with recursive calls for controlled traversal.
2. Technical Filtering: FilterForVisitor
Retrieving content from the tree is only the first step. To maintain security and editorial intent, the resulting collection must be processed through the system's filtering engine.
The Role of FilterForVisitor
The FilterForVisitor class performs three critical security checks, ensuring that:
- The content is in a "Published" state.
- The current user has "Read" access to the content.
- The content is within its "Active" time range (Start/Stop publish dates).
Respecting VisibleInMenu
Editorial governance is maintained through the built-in VisibleInMenu property. Developers must manually check this property during menu generation to allow editors to hide specific technical or landing pages from the primary navigation without deleting or unpublishing them.
3. ViewComponent Implementation Pattern
In CMS 12, the recommended pattern for modular UI elements like navigation is the ViewComponent. This provides a clean separation between retrieval logic and Razor rendering, and supports dependency injection for all required services.
Recursive Menu Logic
A scalable menu implementation often requires recursive logic to handle multi-level dropdowns or mega-menus. The private helper method calls itself for each child's subtree, building the full hierarchy before the view renders.
Important: Uncached recursive calls to GetMenuItems on deep trees execute a database query per level per request. Always wrap the result in fragment caching (see section 5) or an explicit object cache before deploying to production.
4. Breadcrumb Generation
Breadcrumbs enhance UX and SEO by providing clear path indicators. Implementation uses the GetAncestors method to build the path from the current page up to the site's start page, then reverses it to obtain the correct root-to-current display order.
5. Performance and Caching Strategies
Navigation components execute on every page request. In a PaaS environment, unoptimized navigation is one of the most common sources of avoidable database strain.
Object Caching
By using IContentLoader, developers benefit from the built-in Optimizely object cache automatically. However, for complex menus requiring significant computation - such as custom sorting or external metadata merging - implement explicit caching using ISignaledCache or IObjectInstanceCache to avoid recomputation on every hit.
Fragment Caching in Razor
For the final rendered HTML, use ASP.NET Core's cache tag helper. This prevents the entire ViewComponent logic from re-executing for every visitor on every page load.
Pro tip: Use vary-by-user="true" on the cache tag to ensure personalized menus (role-based visibility) are cached per user rather than shared incorrectly across visitor groups.
Conclusion
Creating effective navigation in Optimizely CMS 12 requires efficient retrieval via IContentLoader, strict filtering via FilterForVisitor, and optimized rendering with ViewComponents. By respecting the VisibleInMenu property and implementing robust fragment caching, technical teams ensure that site navigation remains an asset to both the user experience and the system's overall scalability.
