Implementing Navigation and Tab Layout in HarmonyOS

Router Configuration

Add the router map reference in module.json5:

"routerMap": "$profile:route_map"

Route Map Definition

Create route_map.json in resources/base/profile/:

{
  "routerMap": [
    {
      "name": "LaunchScreen",
      "pageSourceFile": "src/main/ets/components/LaunchScreen.ets",
      "buildFunction": "LaunchScreenBuilder",
      "data": {
        "description": "Initial landing page"
      }
    },
    {
      "name": "MainLayout",
      "pageSourceFile": "src/main/ets/components/MainLayout.ets",
      "buildFunction": "MainLayoutBuilder",
      "data": {
        "description": "Main navigation layout"
      }
    }
  ]
}

Launch Screen Component

@Builder
export function LaunchScreenBuilder() {
  LaunchScreen()
}

@Component
struct LaunchScreen {
  pathStack: NavPathStack = new NavPathStack()

  build() {
    NavDestination() {
      Column() {
        Button('Enter Main Layout')
          .onClick(() => {
            this.pathStack.pushPathByName("MainLayout", null, false)
          })
      }
      .justifyContent(FlexAlign.Center)
      .width('100%')
      .height('100%')
    }
    .title('Welcome')
    .onReady((ctx: NavDestinationContext) => {
      this.pathStack = ctx.pathStack
    })
  }
}

Main Layout with Tabs

interface TabItem {
  title: string
  icon: ResourceStr
}

@Builder
export function MainLayoutBuilder() {
  MainLayout()
}

@Component
struct MainLayout {
  navigationStack: NavPathStack = new NavPathStack()

  tabItems: TabItem[] = [
    { title: 'Home', icon: $r('app.media.ic_home') },
    { title: 'Explore', icon: $r('app.media.ic_search') },
    { title: 'Feed', icon: $r('app.media.ic_feed') },
    { title: 'Profile', icon: $r('app.media.ic_user') }
  ]

  @Builder
  tabBarTemplate(item: TabItem) {
    Column({ space: 4 }) {
      Image(item.icon)
        .size({ width: 22, height: 22 })
        .fillColor('#4A9ED4')
      Text(item.title)
        .fontSize(12)
        .fontColor('#4A9ED4')
    }
  }

  build() {
    NavDestination() {
      Tabs({ barPosition: BarPosition.End }) {
        ForEach(this.tabItems, (item: TabItem) => {
          TabContent() {
            this.tabBarTemplate(item)
          }
          .tabBar(this.tabBarTemplate(item))
          .backgroundColor('#1A1B1E')
          .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
        })
      }
      .backgroundColor('#2D2E31')
      .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
    }
    .onReady((context: NavDestinationContext) => {
      this.navigationStack = context.pathStack
    })
  }
}

Application Entry Point

@Entry
@Component
struct EntryPoint {
  navStack: NavPathStack = new NavPathStack()

  build() {
    Navigation(this.navStack) {
      Column() {
        Text('App Container')
      }
      .width('100%')
      .height('100%')
    }
    .onAppear(() => {
      this.navStack.pushPathByName("LaunchScreen", null, false)
    })
    .hideNavBar(true)
  }
}

Navigation Flow

  1. EntryPoint initializes with a hidden Navigation container
  2. On appearance, it automatically pushes LaunchScreen on to the stack
  3. User interaction triggers navigation to MainLayout
  4. MainLayout displays a bottom tab bar with four navigation items

The hideNavBar(true) setting prevants the root Navigation from appearing in the back stack, ensuring the back button returns directly to the system home screen rather than the navigation container.

Tags: HarmonyOS Navigation NavPathStack Tabs ArkUI

Posted on Sun, 10 May 2026 06:57:57 +0000 by silversinner