or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

content-management.mdcore-components.mdindex.mdindicators-submenus.mdinteractive-elements.mdutility-functions.md

content-management.mddocs/

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

```