0
# Portal Rendering and Content Display
1
2
The `TooltipPortal` and `TooltipContent` components work together to render tooltip content outside the normal DOM hierarchy with sophisticated positioning, collision detection, and accessibility features.
3
4
## Capabilities
5
6
### TooltipPortal Component
7
8
Portal component that renders children in a different part of the DOM, typically at the document body level.
9
10
```typescript { .api }
11
/**
12
* Portal component for rendering tooltip content outside normal DOM flow
13
* @param children - Content to portal (typically TooltipContent)
14
* @param container - Container element to portal into (default: document.body)
15
* @param forceMount - Force mounting when controlling animation externally
16
*/
17
interface TooltipPortalProps {
18
children?: React.ReactNode;
19
container?: HTMLElement;
20
forceMount?: true;
21
}
22
23
const TooltipPortal: React.FC<TooltipPortalProps>;
24
```
25
26
### TooltipContent Component
27
28
Main tooltip content component with positioning, collision detection, and accessibility features.
29
30
```typescript { .api }
31
/**
32
* Main tooltip content component with positioning and accessibility
33
* @param children - Content to display in tooltip
34
* @param side - Preferred side relative to trigger
35
* @param sideOffset - Distance in pixels from trigger
36
* @param align - Alignment relative to trigger
37
* @param alignOffset - Offset in pixels along alignment axis
38
* @param avoidCollisions - Whether to avoid viewport collisions
39
* @param collisionBoundary - Boundary elements for collision detection
40
* @param collisionPadding - Padding around collision boundary
41
* @param arrowPadding - Minimum distance between arrow and content edges
42
* @param sticky - Sticky behavior when trigger moves
43
* @param hideWhenDetached - Hide when trigger is no longer visible
44
* @param aria-label - Accessible label for tooltip
45
* @param onEscapeKeyDown - Handler for escape key
46
* @param onPointerDownOutside - Handler for outside pointer events
47
* @param forceMount - Force mounting for animation control
48
*/
49
type TooltipContentElement = React.ComponentRef<"div">;
50
interface TooltipContentProps extends React.ComponentPropsWithoutRef<"div"> {
51
side?: "top" | "right" | "bottom" | "left";
52
sideOffset?: number;
53
align?: "start" | "center" | "end";
54
alignOffset?: number;
55
avoidCollisions?: boolean;
56
collisionBoundary?: Element | Element[];
57
collisionPadding?: number | Partial<Record<"top" | "right" | "bottom" | "left", number>>;
58
arrowPadding?: number;
59
sticky?: "partial" | "always";
60
hideWhenDetached?: boolean;
61
'aria-label'?: string;
62
onEscapeKeyDown?: (event: KeyboardEvent) => void;
63
onPointerDownOutside?: (event: CustomEvent) => void;
64
forceMount?: true;
65
}
66
67
const TooltipContent: React.forwardRef<TooltipContentElement, TooltipContentProps>;
68
```
69
70
**Usage Examples:**
71
72
```typescript
73
import {
74
TooltipProvider,
75
Tooltip,
76
TooltipTrigger,
77
TooltipPortal,
78
TooltipContent,
79
TooltipArrow
80
} from "@radix-ui/react-tooltip";
81
82
// Basic portal and content
83
function BasicTooltip() {
84
return (
85
<TooltipProvider>
86
<Tooltip>
87
<TooltipTrigger>Hover me</TooltipTrigger>
88
<TooltipPortal>
89
<TooltipContent>
90
Simple tooltip content
91
</TooltipContent>
92
</TooltipPortal>
93
</Tooltip>
94
</TooltipProvider>
95
);
96
}
97
98
// Custom positioning
99
function PositionedTooltip() {
100
return (
101
<TooltipProvider>
102
<Tooltip>
103
<TooltipTrigger>Hover me</TooltipTrigger>
104
<TooltipPortal>
105
<TooltipContent
106
side="right"
107
align="start"
108
sideOffset={10}
109
alignOffset={5}
110
>
111
Positioned tooltip
112
<TooltipArrow />
113
</TooltipContent>
114
</TooltipPortal>
115
</Tooltip>
116
</TooltipProvider>
117
);
118
}
119
120
// Collision avoidance
121
function SmartTooltip() {
122
return (
123
<TooltipProvider>
124
<Tooltip>
125
<TooltipTrigger>Near edge</TooltipTrigger>
126
<TooltipPortal>
127
<TooltipContent
128
side="top"
129
avoidCollisions={true}
130
collisionPadding={10}
131
>
132
This tooltip avoids viewport edges
133
</TooltipContent>
134
</TooltipPortal>
135
</Tooltip>
136
</TooltipProvider>
137
);
138
}
139
140
// Custom portal container
141
function CustomPortalTooltip() {
142
const containerRef = useRef<HTMLDivElement>(null);
143
144
return (
145
<div>
146
<div ref={containerRef} className="custom-portal-container" />
147
<TooltipProvider>
148
<Tooltip>
149
<TooltipTrigger>Hover me</TooltipTrigger>
150
<TooltipPortal container={containerRef.current}>
151
<TooltipContent>
152
Rendered in custom container
153
</TooltipContent>
154
</TooltipPortal>
155
</Tooltip>
156
</TooltipProvider>
157
</div>
158
);
159
}
160
161
// Rich content tooltip
162
function RichContentTooltip() {
163
return (
164
<TooltipProvider>
165
<Tooltip>
166
<TooltipTrigger>Product info</TooltipTrigger>
167
<TooltipPortal>
168
<TooltipContent className="rich-tooltip">
169
<div>
170
<h3>Product Details</h3>
171
<p>Price: $29.99</p>
172
<p>In stock: 15 items</p>
173
</div>
174
<TooltipArrow />
175
</TooltipContent>
176
</TooltipPortal>
177
</Tooltip>
178
</TooltipProvider>
179
);
180
}
181
```
182
183
### Portal Props
184
185
#### container
186
187
Specifies container element for portal rendering.
188
189
- **Type**: `HTMLElement`
190
- **Default**: `document.body`
191
- **Description**: Element to render portal content into
192
193
#### forceMount
194
195
Forces mounting regardless of open state for animation control.
196
197
- **Type**: `true`
198
- **Description**: Keeps content mounted for external animation libraries
199
200
### Content Props
201
202
#### Positioning Props
203
204
**side**: Preferred side relative to trigger
205
- **Type**: `"top" | "right" | "bottom" | "left"`
206
- **Default**: `"top"`
207
208
**sideOffset**: Distance from trigger
209
- **Type**: `number`
210
- **Default**: `0`
211
212
**align**: Alignment relative to trigger
213
- **Type**: `"start" | "center" | "end"`
214
- **Default**: `"center"`
215
216
**alignOffset**: Offset along alignment axis
217
- **Type**: `number`
218
- **Default**: `0`
219
220
#### Collision Detection Props
221
222
**avoidCollisions**: Enable collision detection
223
- **Type**: `boolean`
224
- **Default**: `true`
225
226
**collisionBoundary**: Elements to consider for collisions
227
- **Type**: `Element | Element[]`
228
- **Default**: Viewport boundaries
229
230
**collisionPadding**: Padding around collision boundary
231
- **Type**: `number | Partial<Record<"top" | "right" | "bottom" | "left", number>>`
232
- **Default**: `0`
233
234
#### Arrow Props
235
236
**arrowPadding**: Minimum distance between arrow and content edges
237
- **Type**: `number`
238
- **Default**: `0`
239
240
#### Behavior Props
241
242
**sticky**: Sticky behavior when trigger moves
243
- **Type**: `"partial" | "always"`
244
- **Default**: `"partial"`
245
246
**hideWhenDetached**: Hide when trigger is no longer visible
247
- **Type**: `boolean`
248
- **Default**: `false`
249
250
#### Accessibility Props
251
252
**aria-label**: Accessible label
253
- **Type**: `string`
254
- **Description**: Descriptive label for screen readers
255
256
#### Event Props
257
258
**onEscapeKeyDown**: Escape key handler
259
- **Type**: `(event: KeyboardEvent) => void`
260
261
**onPointerDownOutside**: Outside click handler
262
- **Type**: `(event: CustomEvent) => void`
263
264
### Content Features
265
266
#### Grace Area Hover
267
268
Content supports sophisticated hover behavior with grace areas:
269
- Pointer can move from trigger to content without closing
270
- Grace area calculations prevent accidental closing
271
- Smooth user experience when interacting with rich content
272
273
#### CSS Custom Properties
274
275
Content exposes positioning data via CSS custom properties:
276
- `--radix-tooltip-content-transform-origin`
277
- `--radix-tooltip-content-available-width`
278
- `--radix-tooltip-content-available-height`
279
- `--radix-tooltip-trigger-width`
280
- `--radix-tooltip-trigger-height`
281
282
#### Data Attributes
283
284
Content element receives data attributes for styling:
285
- `data-state`: `"closed" | "delayed-open" | "instant-open"`
286
- `data-side`: Current side placement
287
- `data-align`: Current alignment
288
289
### Portal and Content Aliases
290
291
```typescript { .api }
292
const Portal: React.FC<TooltipPortalProps>;
293
const Content: React.forwardRef<TooltipContentElement, TooltipContentProps>;
294
```
295
296
The `Portal` and `Content` components are aliases for shorter import names.
297
298
## Types
299
300
```typescript { .api }
301
type TooltipPortalProps = {
302
children?: React.ReactNode;
303
container?: HTMLElement;
304
forceMount?: true;
305
};
306
307
type TooltipContentElement = React.ComponentRef<"div">;
308
309
type TooltipContentProps = React.ComponentPropsWithoutRef<"div"> & {
310
side?: "top" | "right" | "bottom" | "left";
311
sideOffset?: number;
312
align?: "start" | "center" | "end";
313
alignOffset?: number;
314
avoidCollisions?: boolean;
315
collisionBoundary?: Element | Element[];
316
collisionPadding?: number | Partial<Record<"top" | "right" | "bottom" | "left", number>>;
317
arrowPadding?: number;
318
sticky?: "partial" | "always";
319
hideWhenDetached?: boolean;
320
'aria-label'?: string;
321
onEscapeKeyDown?: (event: KeyboardEvent) => void;
322
onPointerDownOutside?: (event: CustomEvent) => void;
323
forceMount?: true;
324
};
325
```