diff --git a/packages/components/src/components/navigation-item/model.ts b/packages/components/src/components/navigation-item/model.ts
index 807e78df61fd..bdbe8938a970 100644
--- a/packages/components/src/components/navigation-item/model.ts
+++ b/packages/components/src/components/navigation-item/model.ts
@@ -36,6 +36,11 @@ export type DBNavigationItemDefaultProps = {
* This is for mobile navigation only, if it is set the sub-navigation is a static overlay
*/
subNavigationExpanded?: boolean | string;
+
+ /**
+ * ID for the sub-navigation element. If not provided, a deterministic ID will be generated to ensure SSR compatibility.
+ */
+ subNavigationId?: string;
};
export type DBNavigationItemProps = DBNavigationItemDefaultProps &
diff --git a/packages/components/src/components/navigation-item/navigation-item.lite.tsx b/packages/components/src/components/navigation-item/navigation-item.lite.tsx
index af2f46b956da..ba3823a90792 100644
--- a/packages/components/src/components/navigation-item/navigation-item.lite.tsx
+++ b/packages/components/src/components/navigation-item/navigation-item.lite.tsx
@@ -29,7 +29,11 @@ export default function DBNavigationItem(props: DBNavigationItemProps) {
hasSubNavigation: true,
isSubNavigationExpanded: false,
autoClose: false,
- subNavigationId: 'sub-navigation-' + uuid(),
+ // Use deterministic ID generation for SSR compatibility:
+ // 1. Prefer explicit subNavigationId prop
+ // 2. Fallback to component id + suffix
+ // 3. Default to fixed string (instead of random UUID)
+ subNavigationId: props.subNavigationId ?? (props.id ? `${props.id}-sub-navigation` : 'sub-navigation'),
navigationItemSafeTriangle: undefined,
handleNavigationItemClick: (event: any) => {
if (event?.target?.nodeName === 'A') {
@@ -68,6 +72,14 @@ export default function DBNavigationItem(props: DBNavigationItemProps) {
}
}, [props.subNavigationExpanded]);
+ onUpdate(() => {
+ // Update subNavigationId if props change
+ const newSubNavigationId = props.subNavigationId ?? (props.id ? `${props.id}-sub-navigation` : 'sub-navigation');
+ if (state.subNavigationId !== newSubNavigationId) {
+ state.subNavigationId = newSubNavigationId;
+ }
+ }, [props.subNavigationId, props.id]);
+
onUpdate(() => {
if (state.initialized && _ref) {
const subNavigationSlot = _ref.querySelector('menu');
diff --git a/packages/components/src/components/navigation-item/navigation-item.spec.tsx b/packages/components/src/components/navigation-item/navigation-item.spec.tsx
index aa055f6718d4..4fe216a2e67c 100644
--- a/packages/components/src/components/navigation-item/navigation-item.spec.tsx
+++ b/packages/components/src/components/navigation-item/navigation-item.spec.tsx
@@ -29,6 +29,39 @@ const testComponent = () => {
const component = await mount(comp);
await expect(component).toHaveScreenshot();
});
+
+ test('should use deterministic subNavigationId when id prop is provided', async ({ mount }) => {
+ const componentWithId = (
+
+ Test
+
+ );
+ const component = await mount(componentWithId);
+ const subNav = component.locator('menu[id="test-nav-item-sub-navigation"]');
+ await expect(subNav).toBeAttached();
+ });
+
+ test('should use provided subNavigationId when specified', async ({ mount }) => {
+ const componentWithSubNavId = (
+
+ Test
+
+ );
+ const component = await mount(componentWithSubNavId);
+ const subNav = component.locator('menu[id="custom-sub-nav-id"]');
+ await expect(subNav).toBeAttached();
+ });
+
+ test('should use fallback subNavigationId when no id or subNavigationId provided', async ({ mount }) => {
+ const componentWithoutId = (
+
+ Test
+
+ );
+ const component = await mount(componentWithoutId);
+ const subNav = component.locator('menu[id="sub-navigation"]');
+ await expect(subNav).toBeAttached();
+ });
};
const testA11y = () => {
test('should have same aria-snapshot', async ({ mount }, testInfo) => {