Illustrative examples
Below are short, focused snippets that demonstrate the recommended registration and server-side active-state pattern.
Feature module
TypeScript
1// features/eagles/eagles.module.ts2import { Module } from '@nestjs/common';3import { EaglesController } from './eagles.controller';4import { EaglesService } from './eagles.service';5import {6 NavigationService,7 NavigationRegistry,8 AutoRegisterModule,9} from '@harpy-js/core';1011@Module({12 controllers: [EaglesController],13 providers: [EaglesService],14})15export class EaglesModule extends AutoRegisterModule {16 constructor(17 navigationService: NavigationService,18 private readonly eaglesService: EaglesService,19 ) {20 super(navigationService);21 }2223 protected registerNavigation(navigation: NavigationRegistry): void {24 // Delegate to the feature service; AutoRegisterModule will call this25 this.eaglesService.registerNavigation(navigation);26 }27}Feature service
TypeScript
1// features/eagles/eagles.service.ts2import { Injectable } from '@nestjs/common';3import { NavigationRegistry } from '@harpy-js/core';45@Injectable()6export class EaglesService {7 /**8 * Register feature documentation in the shared navigation9 * This is called during module initialization (OnModuleInit)10 */11 registerNavigation(navigationService: NavigationRegistry) {12 // Add this feature to the Core Concepts section13 navigationService.addItemToSection('core-concepts', {14 id: 'eagles',15 title: 'Eagles (Feature Example)',16 href: '/docs/eagles',17 });18 }19}Controller / SSR
Compute the current path on the server and ask the navigation service for decorated sections (with active flags) so the sidebar is correct on first render.
TypeScript
1// features/eagles/eagles.controller.ts2import { Controller, Get, Req } from '@nestjs/common';3import { JsxRender } from '@harpy-js/core';4import EaglesPage from './views/eagles-page';5import DashboardLayout from '../../layouts/dashboard-layout';6import { NavigationService } from '@harpy-js/core';7import { getDictionary } from '../../i18n/get-dictionary';89@Controller('docs')10export class EaglesController {11 constructor(private readonly navigationService: NavigationService) {}1213 @Get('eagles')14 @JsxRender(EaglesPage, { layout: DashboardLayout })15 async eagles(16 @Req() req: FastifyRequest,17 @CurrentLocale() locale: string,18 ) {19 const currentPath = (req.originalUrl || req.url);2021 // Load the dictionary for the current locale22 const dict = await getDictionary(locale);2324 // Returns sections where items have '.active === true' for the25 // item that matches 'currentPath'.26 const sections = this.navigationService.getSectionsForRoute(currentPath);27 const activeItemId = this.navigationService.getActiveItemId(currentPath);2829 return { 30 sections,31 dict,32 locale,33 activeItemId,34 };35 }36}Advanced tips
You can register sections early, use explicit ordering, or reorder at runtime. The examples below show each approach.
Item types & priorities
TypeScript
1// packages/harpy-core/src/core/types/nav.types.ts2export interface NavSection {3 id: string;4 title: string;5 items: NavItem[];6 order?: number; // lower numbers appear earlier7}Move at runtime
TypeScript
1// packages/harpy-core/src/core/navigation.service.ts2// move an existing section to the front3navigationService.moveSectionToFront('core-concepts');Benefits
- No duplication: register once, render everywhere.
- Scalable: features are self-contained.
- SSR-friendly: compute
activeserver-side to avoid hydration mismatch.