0
# Content Management
1
2
Content and viewport components for displaying menu content with animation support and focus management.
3
4
## Capabilities
5
6
### NavigationMenuContent
7
8
Dismissable content panel that displays when a navigation menu item is triggered, with built-in focus management and animation support.
9
10
```typescript { .api }
11
/**
12
* Content panel for navigation menu items with dismissable layer integration
13
* @param forceMount - Force mounting for animation control (useful with animation libraries)
14
*/
15
const NavigationMenuContent: React.ForwardRefExoticComponent<
16
NavigationMenuContentProps & React.RefAttributes<HTMLDivElement>
17
>;
18
19
interface NavigationMenuContentProps
20
extends React.ComponentPropsWithoutRef<"div"> {
21
forceMount?: true;
22
}
23
```
24
25
**Key Features:**
26
- Dismissable layer integration (clicks outside, Escape key)
27
- Focus management and tab order handling
28
- Animation support via `forceMount` prop
29
- Portal rendering (when used with viewport)
30
- Keyboard navigation within content
31
- Pointer interaction handling
32
33
**Usage Examples:**
34
35
```typescript
36
import {
37
NavigationMenu,
38
NavigationMenuList,
39
NavigationMenuItem,
40
NavigationMenuTrigger,
41
NavigationMenuContent,
42
NavigationMenuLink
43
} from "@radix-ui/react-navigation-menu";
44
45
// Basic content usage
46
function BasicContentExample() {
47
return (
48
<NavigationMenu>
49
<NavigationMenuList>
50
<NavigationMenuItem>
51
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
52
<NavigationMenuContent className="nav-content">
53
<div className="content-grid">
54
<NavigationMenuLink href="/web">Web Development</NavigationMenuLink>
55
<NavigationMenuLink href="/mobile">Mobile Apps</NavigationMenuLink>
56
<NavigationMenuLink href="/desktop">Desktop Software</NavigationMenuLink>
57
</div>
58
</NavigationMenuContent>
59
</NavigationMenuItem>
60
</NavigationMenuList>
61
</NavigationMenu>
62
);
63
}
64
65
// Content with force mount for animations
66
function AnimatedContentExample() {
67
return (
68
<NavigationMenuItem>
69
<NavigationMenuTrigger>Services</NavigationMenuTrigger>
70
<NavigationMenuContent
71
forceMount
72
className="animated-content"
73
onPointerEnter={() => console.log("Content entered")}
74
onPointerLeave={() => console.log("Content left")}
75
>
76
<div className="service-list">
77
<h3>Our Services</h3>
78
<NavigationMenuLink href="/consulting">Consulting</NavigationMenuLink>
79
<NavigationMenuLink href="/support">Support</NavigationMenuLink>
80
<NavigationMenuLink href="/training">Training</NavigationMenuLink>
81
</div>
82
</NavigationMenuContent>
83
</NavigationMenuItem>
84
);
85
}
86
87
// Rich content with complex layout
88
function RichContentExample() {
89
return (
90
<NavigationMenuItem>
91
<NavigationMenuTrigger>Resources</NavigationMenuTrigger>
92
<NavigationMenuContent>
93
<div className="resource-content">
94
<div className="resource-section">
95
<h4>Documentation</h4>
96
<NavigationMenuLink href="/docs/getting-started">Getting Started</NavigationMenuLink>
97
<NavigationMenuLink href="/docs/api">API Reference</NavigationMenuLink>
98
<NavigationMenuLink href="/docs/examples">Examples</NavigationMenuLink>
99
</div>
100
101
<div className="resource-section">
102
<h4>Community</h4>
103
<NavigationMenuLink href="/forum">Forum</NavigationMenuLink>
104
<NavigationMenuLink href="/discord">Discord</NavigationMenuLink>
105
<NavigationMenuLink href="/github">GitHub</NavigationMenuLink>
106
</div>
107
108
<div className="resource-section">
109
<h4>Latest</h4>
110
<p>Check out our latest blog post about navigation patterns.</p>
111
<NavigationMenuLink href="/blog/latest">Read More</NavigationMenuLink>
112
</div>
113
</div>
114
</NavigationMenuContent>
115
</NavigationMenuItem>
116
);
117
}
118
```
119
120
### NavigationMenuViewport
121
122
Centralized viewport container that manages and displays menu content with dynamic sizing and positioning.
123
124
```typescript { .api }
125
/**
126
* Centralized viewport for menu content with dynamic sizing
127
* Provides a container that automatically sizes to match active content
128
* @param forceMount - Force mounting for animation control
129
*/
130
const NavigationMenuViewport: React.ForwardRefExoticComponent<
131
NavigationMenuViewportProps & React.RefAttributes<HTMLDivElement>
132
>;
133
134
interface NavigationMenuViewportProps
135
extends React.ComponentPropsWithoutRef<"div"> {
136
forceMount?: true;
137
}
138
```
139
140
**Key Features:**
141
- Dynamic sizing based on active content
142
- CSS custom properties for dimensions
143
- Portal rendering for content
144
- Animation support with presence detection
145
- Pointer event handling
146
- Content lifecycle management
147
148
**Usage Examples:**
149
150
```typescript
151
import {
152
NavigationMenu,
153
NavigationMenuList,
154
NavigationMenuItem,
155
NavigationMenuTrigger,
156
NavigationMenuContent,
157
NavigationMenuViewport
158
} from "@radix-ui/react-navigation-menu";
159
160
// Basic viewport setup
161
function ViewportExample() {
162
return (
163
<div className="nav-container">
164
<NavigationMenu>
165
<NavigationMenuList>
166
<NavigationMenuItem>
167
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
168
<NavigationMenuContent>
169
<div className="products-content">
170
{/* Content will be rendered in viewport */}
171
Product information here
172
</div>
173
</NavigationMenuContent>
174
</NavigationMenuItem>
175
176
<NavigationMenuItem>
177
<NavigationMenuTrigger>Services</NavigationMenuTrigger>
178
<NavigationMenuContent>
179
<div className="services-content">
180
{/* Different sized content */}
181
Much larger service information content here
182
with multiple sections and detailed descriptions
183
</div>
184
</NavigationMenuContent>
185
</NavigationMenuItem>
186
</NavigationMenuList>
187
188
{/* Viewport renders all content and manages transitions */}
189
<NavigationMenuViewport className="nav-viewport" />
190
</NavigationMenu>
191
</div>
192
);
193
}
194
195
// Viewport with custom styling using CSS custom properties
196
function StyledViewportExample() {
197
return (
198
<NavigationMenu>
199
<NavigationMenuList>
200
<NavigationMenuItem>
201
<NavigationMenuTrigger>Large Content</NavigationMenuTrigger>
202
<NavigationMenuContent>
203
<div style={{ width: "400px", height: "300px" }}>
204
Large content area
205
</div>
206
</NavigationMenuContent>
207
</NavigationMenuItem>
208
</NavigationMenuList>
209
210
<NavigationMenuViewport
211
className="custom-viewport"
212
style={{
213
// CSS custom properties are automatically set:
214
// --radix-navigation-menu-viewport-width
215
// --radix-navigation-menu-viewport-height
216
width: "var(--radix-navigation-menu-viewport-width)",
217
height: "var(--radix-navigation-menu-viewport-height)",
218
transition: "width 200ms, height 200ms",
219
}}
220
/>
221
</NavigationMenu>
222
);
223
}
224
225
// Viewport with animation control
226
function AnimatedViewportExample() {
227
return (
228
<NavigationMenu>
229
<NavigationMenuList>
230
<NavigationMenuItem>
231
<NavigationMenuTrigger>Animated Content</NavigationMenuTrigger>
232
<NavigationMenuContent forceMount>
233
Animated content here
234
</NavigationMenuContent>
235
</NavigationMenuItem>
236
</NavigationMenuList>
237
238
<NavigationMenuViewport
239
forceMount
240
onPointerEnter={() => console.log("Viewport entered")}
241
onPointerLeave={() => console.log("Viewport left")}
242
/>
243
</NavigationMenu>
244
);
245
}
246
```
247
248
## Content vs Viewport Mode
249
250
The navigation menu can operate in two content display modes:
251
252
### Direct Content Mode (Default)
253
254
Content is rendered directly adjacent to triggers:
255
256
```typescript
257
<NavigationMenuItem>
258
<NavigationMenuTrigger>Trigger</NavigationMenuTrigger>
259
<NavigationMenuContent>
260
{/* Rendered directly in DOM tree */}
261
</NavigationMenuContent>
262
</NavigationMenuItem>
263
```
264
265
### Viewport Mode
266
267
Content is rendered centrally in a viewport:
268
269
```typescript
270
<NavigationMenu>
271
<NavigationMenuList>
272
<NavigationMenuItem>
273
<NavigationMenuTrigger>Trigger</NavigationMenuTrigger>
274
<NavigationMenuContent>
275
{/* Rendered via portal into viewport */}
276
</NavigationMenuContent>
277
</NavigationMenuItem>
278
</NavigationMenuList>
279
280
<NavigationMenuViewport />
281
</NavigationMenu>
282
```
283
284
**Benefits of Viewport Mode:**
285
- Consistent content positioning
286
- Smooth size transitions between different content
287
- Better animation control
288
- Simplified layout management
289
290
## Event Handling
291
292
### Content Events
293
294
```typescript
295
// Pointer events for hover behavior
296
onPointerEnter?: (event: React.PointerEvent) => void;
297
onPointerLeave?: (event: React.PointerEvent) => void;
298
299
// Focus events for accessibility
300
onFocusOutside?: (event: Event) => void;
301
onPointerDownOutside?: (event: Event) => void;
302
303
// Keyboard events
304
onKeyDown?: (event: React.KeyboardEvent) => void;
305
onEscapeKeyDown?: (event: KeyboardEvent) => void;
306
```
307
308
### Viewport Events
309
310
```typescript
311
// Pointer events
312
onPointerEnter?: (event: React.PointerEvent) => void;
313
onPointerLeave?: (event: React.PointerEvent) => void;
314
```
315
316
## CSS Custom Properties
317
318
When using viewport mode, these CSS custom properties are automatically available:
319
320
```css
321
.nav-viewport {
322
width: var(--radix-navigation-menu-viewport-width);
323
height: var(--radix-navigation-menu-viewport-height);
324
transition: width 200ms, height 200ms;
325
}
326
```
327
328
## Data Attributes
329
330
Content components expose data attributes for styling and animation:
331
332
### NavigationMenuContent Data Attributes
333
334
```css
335
[data-state="open"] { /* Content is visible */ }
336
[data-state="closed"] { /* Content is hidden */ }
337
[data-motion="from-start"] { /* Animating in from start */ }
338
[data-motion="from-end"] { /* Animating in from end */ }
339
[data-motion="to-start"] { /* Animating out to start */ }
340
[data-motion="to-end"] { /* Animating out to end */ }
341
[data-orientation="horizontal"|"vertical"] { /* Content orientation */ }
342
```
343
344
**Animation Examples:**
345
346
```css
347
.nav-content[data-state="open"] {
348
animation: slideIn 200ms ease-out;
349
}
350
351
.nav-content[data-state="closed"] {
352
animation: slideOut 200ms ease-in;
353
}
354
355
.nav-content[data-motion="from-start"] {
356
animation: slideFromStart 200ms ease-out;
357
}
358
359
.nav-content[data-motion="from-end"] {
360
animation: slideFromEnd 200ms ease-out;
361
}
362
363
@keyframes slideIn {
364
from { opacity: 0; transform: translateY(-10px); }
365
to { opacity: 1; transform: translateY(0); }
366
}
367
368
@keyframes slideFromStart {
369
from { transform: translateX(-20px); opacity: 0; }
370
to { transform: translateX(0); opacity: 1; }
371
}
372
```
373
374
### NavigationMenuViewport Data Attributes
375
376
```css
377
[data-state="open"] { /* Viewport contains content */ }
378
[data-state="closed"] { /* Viewport is empty */ }
379
[data-orientation="horizontal"|"vertical"] { /* Viewport orientation */ }
380
```
381
382
**Viewport Styling:**
383
384
```css
385
.nav-viewport[data-state="open"] {
386
pointer-events: auto;
387
opacity: 1;
388
}
389
390
.nav-viewport[data-state="closed"] {
391
pointer-events: none;
392
opacity: 0;
393
}
394
395
.nav-viewport[data-orientation="horizontal"] {
396
/* Styles for horizontal viewport */
397
}
398
399
.nav-viewport[data-orientation="vertical"] {
400
/* Styles for vertical viewport */
401
}
402
```