0
# Visual Indicators and Sub-menus
1
2
Indicator component for visual feedback and sub-menu support for nested navigation structures.
3
4
## Capabilities
5
6
### NavigationMenuIndicator
7
8
Visual indicator component that shows the active menu item with automatic positioning and animation support.
9
10
```typescript { .api }
11
/**
12
* Visual indicator that highlights the active menu item
13
* Automatically positions itself relative to the active trigger
14
* @param forceMount - Force mounting for animation control
15
*/
16
const NavigationMenuIndicator: React.ForwardRefExoticComponent<
17
NavigationMenuIndicatorProps & React.RefAttributes<HTMLDivElement>
18
>;
19
20
interface NavigationMenuIndicatorProps
21
extends React.ComponentPropsWithoutRef<"div"> {
22
forceMount?: true;
23
}
24
```
25
26
**Key Features:**
27
- Automatic positioning based on active trigger
28
- Smooth transitions between different menu items
29
- Portal rendering to indicator track
30
- Orientation support (horizontal/vertical)
31
- Animation support via `forceMount`
32
- Resize observer integration for responsive positioning
33
34
**Usage Examples:**
35
36
```typescript
37
import {
38
NavigationMenu,
39
NavigationMenuList,
40
NavigationMenuItem,
41
NavigationMenuTrigger,
42
NavigationMenuContent,
43
NavigationMenuIndicator
44
} from "@radix-ui/react-navigation-menu";
45
46
// Basic indicator usage
47
function IndicatorExample() {
48
return (
49
<NavigationMenu>
50
<NavigationMenuList>
51
<NavigationMenuItem>
52
<NavigationMenuTrigger>Home</NavigationMenuTrigger>
53
<NavigationMenuContent>Home content</NavigationMenuContent>
54
</NavigationMenuItem>
55
56
<NavigationMenuItem>
57
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
58
<NavigationMenuContent>Products content</NavigationMenuContent>
59
</NavigationMenuItem>
60
61
<NavigationMenuItem>
62
<NavigationMenuTrigger>Services</NavigationMenuTrigger>
63
<NavigationMenuContent>Services content</NavigationMenuContent>
64
</NavigationMenuItem>
65
66
{/* Indicator automatically positions over active trigger */}
67
<NavigationMenuIndicator className="nav-indicator" />
68
</NavigationMenuList>
69
</NavigationMenu>
70
);
71
}
72
73
// Styled indicator with animations
74
function StyledIndicatorExample() {
75
return (
76
<NavigationMenu>
77
<NavigationMenuList className="nav-list">
78
<NavigationMenuItem>
79
<NavigationMenuTrigger>Tab 1</NavigationMenuTrigger>
80
<NavigationMenuContent>Content 1</NavigationMenuContent>
81
</NavigationMenuItem>
82
83
<NavigationMenuItem>
84
<NavigationMenuTrigger>Tab 2</NavigationMenuTrigger>
85
<NavigationMenuContent>Content 2</NavigationMenuContent>
86
</NavigationMenuItem>
87
88
<NavigationMenuIndicator
89
className="indicator"
90
style={{
91
bottom: 0,
92
height: "2px",
93
backgroundColor: "blue",
94
transition: "transform 200ms ease",
95
}}
96
/>
97
</NavigationMenuList>
98
</NavigationMenu>
99
);
100
}
101
102
// Force mounted indicator for custom animations
103
function AnimatedIndicatorExample() {
104
return (
105
<NavigationMenu>
106
<NavigationMenuList>
107
<NavigationMenuItem>
108
<NavigationMenuTrigger>Item 1</NavigationMenuTrigger>
109
<NavigationMenuContent>Content 1</NavigationMenuContent>
110
</NavigationMenuItem>
111
112
<NavigationMenuItem>
113
<NavigationMenuTrigger>Item 2</NavigationMenuTrigger>
114
<NavigationMenuContent>Content 2</NavigationMenuContent>
115
</NavigationMenuItem>
116
117
<NavigationMenuIndicator
118
forceMount
119
className="custom-animated-indicator"
120
/>
121
</NavigationMenuList>
122
</NavigationMenu>
123
);
124
}
125
```
126
127
### Vertical Orientation Indicator
128
129
```typescript
130
function VerticalIndicatorExample() {
131
return (
132
<NavigationMenu orientation="vertical">
133
<NavigationMenuList>
134
<NavigationMenuItem>
135
<NavigationMenuTrigger>Vertical Item 1</NavigationMenuTrigger>
136
<NavigationMenuContent>Content 1</NavigationMenuContent>
137
</NavigationMenuItem>
138
139
<NavigationMenuItem>
140
<NavigationMenuTrigger>Vertical Item 2</NavigationMenuTrigger>
141
<NavigationMenuContent>Content 2</NavigationMenuContent>
142
</NavigationMenuItem>
143
144
{/* Indicator adapts to vertical orientation */}
145
<NavigationMenuIndicator
146
style={{
147
left: 0,
148
width: "3px",
149
backgroundColor: "green",
150
transition: "transform 200ms ease",
151
}}
152
/>
153
</NavigationMenuList>
154
</NavigationMenu>
155
);
156
}
157
```
158
159
## Sub-menu Architecture
160
161
Sub-menus inherit from the same core architecture but operate within a nested context:
162
163
### Basic Sub-menu Structure
164
165
```typescript
166
import {
167
NavigationMenu,
168
NavigationMenuList,
169
NavigationMenuItem,
170
NavigationMenuTrigger,
171
NavigationMenuContent,
172
NavigationMenuSub
173
} from "@radix-ui/react-navigation-menu";
174
175
function SubMenuExample() {
176
return (
177
<NavigationMenu>
178
<NavigationMenuList>
179
<NavigationMenuItem>
180
<NavigationMenuTrigger>Main Category</NavigationMenuTrigger>
181
<NavigationMenuContent>
182
{/* Sub-menu within content */}
183
<NavigationMenuSub>
184
<NavigationMenuList>
185
<NavigationMenuItem>
186
<NavigationMenuTrigger>Subcategory 1</NavigationMenuTrigger>
187
<NavigationMenuContent>
188
Sub-content 1
189
</NavigationMenuContent>
190
</NavigationMenuItem>
191
192
<NavigationMenuItem>
193
<NavigationMenuTrigger>Subcategory 2</NavigationMenuTrigger>
194
<NavigationMenuContent>
195
Sub-content 2
196
</NavigationMenuContent>
197
</NavigationMenuItem>
198
</NavigationMenuList>
199
</NavigationMenuSub>
200
</NavigationMenuContent>
201
</NavigationMenuItem>
202
</NavigationMenuList>
203
</NavigationMenu>
204
);
205
}
206
```
207
208
### Controlled Sub-menu
209
210
```typescript
211
function ControlledSubMenuExample() {
212
const [subValue, setSubValue] = React.useState("");
213
214
return (
215
<NavigationMenu>
216
<NavigationMenuList>
217
<NavigationMenuItem>
218
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
219
<NavigationMenuContent>
220
<NavigationMenuSub
221
value={subValue}
222
onValueChange={setSubValue}
223
orientation="vertical"
224
>
225
<NavigationMenuList>
226
<NavigationMenuItem value="web">
227
<NavigationMenuTrigger>Web Products</NavigationMenuTrigger>
228
<NavigationMenuContent>
229
Web product details
230
</NavigationMenuContent>
231
</NavigationMenuItem>
232
233
<NavigationMenuItem value="mobile">
234
<NavigationMenuTrigger>Mobile Products</NavigationMenuTrigger>
235
<NavigationMenuContent>
236
Mobile product details
237
</NavigationMenuContent>
238
</NavigationMenuItem>
239
</NavigationMenuList>
240
</NavigationMenuSub>
241
</NavigationMenuContent>
242
</NavigationMenuItem>
243
</NavigationMenuList>
244
</NavigationMenu>
245
);
246
}
247
```
248
249
### Complex Nested Structure
250
251
```typescript
252
function ComplexNestedExample() {
253
return (
254
<NavigationMenu>
255
<NavigationMenuList>
256
<NavigationMenuItem>
257
<NavigationMenuTrigger>Solutions</NavigationMenuTrigger>
258
<NavigationMenuContent>
259
<div className="mega-menu">
260
<div className="menu-column">
261
<h3>By Industry</h3>
262
<NavigationMenuSub>
263
<NavigationMenuList>
264
<NavigationMenuItem>
265
<NavigationMenuTrigger>Healthcare</NavigationMenuTrigger>
266
<NavigationMenuContent>
267
<NavigationMenuLink href="/healthcare/hospitals">Hospitals</NavigationMenuLink>
268
<NavigationMenuLink href="/healthcare/clinics">Clinics</NavigationMenuLink>
269
</NavigationMenuContent>
270
</NavigationMenuItem>
271
272
<NavigationMenuItem>
273
<NavigationMenuTrigger>Finance</NavigationMenuTrigger>
274
<NavigationMenuContent>
275
<NavigationMenuLink href="/finance/banking">Banking</NavigationMenuLink>
276
<NavigationMenuLink href="/finance/insurance">Insurance</NavigationMenuLink>
277
</NavigationMenuContent>
278
</NavigationMenuItem>
279
</NavigationMenuList>
280
</NavigationMenuSub>
281
</div>
282
283
<div className="menu-column">
284
<h3>By Size</h3>
285
<NavigationMenuSub>
286
<NavigationMenuList>
287
<NavigationMenuItem>
288
<NavigationMenuLink href="/enterprise">Enterprise</NavigationMenuLink>
289
</NavigationMenuItem>
290
291
<NavigationMenuItem>
292
<NavigationMenuLink href="/small-business">Small Business</NavigationMenuLink>
293
</NavigationMenuItem>
294
</NavigationMenuList>
295
</NavigationMenuSub>
296
</div>
297
</div>
298
</NavigationMenuContent>
299
</NavigationMenuItem>
300
</NavigationMenuList>
301
</NavigationMenu>
302
);
303
}
304
```
305
306
## Indicator Positioning
307
308
The indicator automatically handles positioning based on the active trigger:
309
310
### Horizontal Layout
311
- Uses `transform: translateX()` for positioning
312
- Width matches the active trigger width
313
- Positioned at bottom by default
314
315
### Vertical Layout
316
- Uses `transform: translateY()` for positioning
317
- Height matches the active trigger height
318
- Positioned at left by default
319
320
### CSS Example
321
322
```css
323
/* Horizontal indicator */
324
.nav-indicator[data-orientation="horizontal"] {
325
bottom: 0;
326
height: 2px;
327
background: var(--accent-color);
328
transition: transform 200ms ease;
329
}
330
331
/* Vertical indicator */
332
.nav-indicator[data-orientation="vertical"] {
333
left: 0;
334
width: 3px;
335
background: var(--accent-color);
336
transition: transform 200ms ease;
337
}
338
339
/* Indicator states */
340
.nav-indicator[data-state="visible"] {
341
opacity: 1;
342
}
343
344
.nav-indicator[data-state="hidden"] {
345
opacity: 0;
346
}
347
```
348
349
## Data Attributes
350
351
Both indicators and sub-menus expose data attributes for styling:
352
353
### NavigationMenuIndicator Data Attributes
354
355
```css
356
[data-state="visible"] { /* Indicator is shown */ }
357
[data-state="hidden"] { /* Indicator is hidden */ }
358
[data-orientation="horizontal"] { /* Horizontal layout */ }
359
[data-orientation="vertical"] { /* Vertical layout */ }
360
```
361
362
**Advanced Indicator Styling:**
363
364
```css
365
.nav-indicator {
366
position: absolute;
367
transition: all 200ms ease;
368
}
369
370
.nav-indicator[data-state="visible"] {
371
opacity: 1;
372
transform: scale(1);
373
}
374
375
.nav-indicator[data-state="hidden"] {
376
opacity: 0;
377
transform: scale(0.8);
378
}
379
380
.nav-indicator[data-orientation="horizontal"] {
381
width: 100%;
382
height: 2px;
383
bottom: 0;
384
left: 0;
385
}
386
387
.nav-indicator[data-orientation="vertical"] {
388
width: 3px;
389
height: 100%;
390
left: 0;
391
top: 0;
392
}
393
```
394
395
### NavigationMenuSub Data Attributes
396
397
```css
398
[data-orientation="horizontal"] { /* Sub-menu horizontal layout */ }
399
[data-orientation="vertical"] { /* Sub-menu vertical layout */ }
400
```
401
402
**Sub-menu Styling:**
403
404
```css
405
.nav-sub[data-orientation="horizontal"] {
406
display: flex;
407
flex-direction: row;
408
}
409
410
.nav-sub[data-orientation="vertical"] {
411
display: flex;
412
flex-direction: column;
413
}
414
```
415
416
These attributes enable CSS-based styling and animations without JavaScript.