or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context-management.mdhook-interface.mdindex.mdpositioned-elements.mdreference-handling.md

hook-interface.mddocs/

0

# Hook Interface

1

2

Programmatic hook interface for low-level control over Popper instances without component wrappers. The usePopper hook provides direct access to Popper.js functionality with React integration, ideal for custom implementations and advanced use cases.

3

4

## Capabilities

5

6

### usePopper Hook

7

8

Provides programmatic control over Popper instances with automatic lifecycle management and state synchronization.

9

10

```typescript { .api }

11

/**

12

* Low-level hook for programmatic control over Popper instances

13

* @param referenceElement - The reference element to position relative to

14

* @param popperElement - The popper element to position

15

* @param options - Configuration options for the Popper instance

16

* @returns Object containing styles, attributes, state, and control functions

17

*/

18

function usePopper<Modifiers>(

19

referenceElement?: Element | PopperJS.VirtualElement | null,

20

popperElement?: HTMLElement | null,

21

options?: Omit<Partial<PopperJS.Options>, 'modifiers'> & {

22

createPopper?: typeof PopperJS.createPopper;

23

modifiers?: ReadonlyArray<Modifier<Modifiers>>;

24

}

25

): UsePopperResult;

26

27

interface UsePopperResult {

28

/** Computed styles for all positioned elements (popper, arrow, etc.) */

29

styles: { [key: string]: React.CSSProperties };

30

/** HTML attributes for elements (data-popper-placement, etc.) */

31

attributes: { [key: string]: { [key: string]: string } | undefined };

32

/** Current Popper.js state object (null if not initialized) */

33

state: PopperJS.State | null;

34

/** Function to manually update popper positioning */

35

update: PopperJS.Instance['update'] | null;

36

/** Function to force immediate positioning update */

37

forceUpdate: PopperJS.Instance['forceUpdate'] | null;

38

}

39

```

40

41

### Hook Options

42

43

The usePopper hook accepts all standard Popper.js options plus React-specific enhancements:

44

45

```typescript { .api }

46

interface UsePopperOptions {

47

/** Preferred placement for the popper element */

48

placement?: PopperJS.Placement;

49

/** Positioning strategy - 'absolute' or 'fixed' */

50

strategy?: PopperJS.PositioningStrategy;

51

/** Array of Popper.js modifiers */

52

modifiers?: ReadonlyArray<Modifier<any>>;

53

/** Callback fired after first positioning update */

54

onFirstUpdate?: (state: Partial<PopperJS.State>) => void;

55

/** Custom createPopper function (for custom Popper builds) */

56

createPopper?: typeof PopperJS.createPopper;

57

}

58

```

59

60

**Usage Examples:**

61

62

```tsx

63

import React from "react";

64

import { usePopper } from "react-popper";

65

66

// Basic hook usage

67

function BasicHookExample() {

68

const [referenceElement, setReferenceElement] = React.useState(null);

69

const [popperElement, setPopperElement] = React.useState(null);

70

const [arrowElement, setArrowElement] = React.useState(null);

71

72

const { styles, attributes } = usePopper(referenceElement, popperElement, {

73

placement: "top",

74

modifiers: [

75

{ name: "arrow", options: { element: arrowElement } },

76

{ name: "offset", options: { offset: [0, 8] } },

77

],

78

});

79

80

return (

81

<>

82

<button ref={setReferenceElement}>Reference Button</button>

83

<div

84

ref={setPopperElement}

85

style={styles.popper}

86

{...attributes.popper}

87

>

88

Popper content

89

<div ref={setArrowElement} style={styles.arrow} />

90

</div>

91

</>

92

);

93

}

94

95

// Advanced hook with state management

96

function AdvancedHookExample() {

97

const [referenceElement, setReferenceElement] = React.useState(null);

98

const [popperElement, setPopperElement] = React.useState(null);

99

const [visible, setVisible] = React.useState(false);

100

101

const { styles, attributes, state, update, forceUpdate } = usePopper(

102

referenceElement,

103

popperElement,

104

{

105

placement: "bottom-start",

106

strategy: "fixed",

107

modifiers: [

108

{

109

name: "preventOverflow",

110

options: {

111

boundary: "viewport",

112

padding: 8,

113

},

114

},

115

{

116

name: "flip",

117

options: {

118

fallbackPlacements: ["top-start", "bottom-end", "top-end"],

119

},

120

},

121

],

122

onFirstUpdate: (state) => {

123

console.log("Initial popper state:", state);

124

},

125

}

126

);

127

128

// Manually update positioning when needed

129

const handleUpdate = async () => {

130

if (update) {

131

const newState = await update();

132

console.log("Updated state:", newState);

133

}

134

};

135

136

// Force update immediately

137

const handleForceUpdate = () => {

138

if (forceUpdate) {

139

const newState = forceUpdate();

140

console.log("Force updated state:", newState);

141

}

142

};

143

144

return (

145

<div>

146

<button

147

ref={setReferenceElement}

148

onClick={() => setVisible(!visible)}

149

>

150

Toggle Popper

151

</button>

152

153

{visible && (

154

<div

155

ref={setPopperElement}

156

style={{

157

...styles.popper,

158

background: "white",

159

border: "1px solid #ccc",

160

borderRadius: "4px",

161

padding: "12px",

162

boxShadow: "0 2px 8px rgba(0,0,0,0.1)",

163

zIndex: 1000,

164

}}

165

{...attributes.popper}

166

>

167

<div>Advanced Popper Content</div>

168

{state && (

169

<div style={{ fontSize: "12px", color: "#666", marginTop: "8px" }}>

170

<div>Placement: {state.placement}</div>

171

<div>Strategy: {state.options.strategy}</div>

172

</div>

173

)}

174

<div style={{ marginTop: "8px" }}>

175

<button onClick={handleUpdate}>Update</button>

176

<button onClick={handleForceUpdate}>Force Update</button>

177

</div>

178

</div>

179

)}

180

</div>

181

);

182

}

183

184

// Custom createPopper function

185

function CustomPopperExample() {

186

const [referenceElement, setReferenceElement] = React.useState(null);

187

const [popperElement, setPopperElement] = React.useState(null);

188

189

// Custom Popper configuration

190

const customCreatePopper = React.useCallback(

191

(reference, popper, options) => {

192

// Add custom logic or use a custom Popper build

193

return PopperJS.createPopper(reference, popper, {

194

...options,

195

// Add default modifiers or override behavior

196

modifiers: [

197

...options.modifiers,

198

{

199

name: "customModifier",

200

enabled: true,

201

phase: "beforeWrite",

202

fn: ({ state }) => {

203

// Custom positioning logic

204

console.log("Custom modifier executed:", state);

205

},

206

},

207

],

208

});

209

},

210

[]

211

);

212

213

const { styles, attributes } = usePopper(referenceElement, popperElement, {

214

createPopper: customCreatePopper,

215

placement: "top",

216

});

217

218

return (

219

<>

220

<button ref={setReferenceElement}>Custom Popper</button>

221

<div

222

ref={setPopperElement}

223

style={styles.popper}

224

{...attributes.popper}

225

>

226

Custom Popper Content

227

</div>

228

</>

229

);

230

}

231

```

232

233

### Virtual Elements

234

235

The usePopper hook supports virtual elements for positioning relative to coordinates rather than DOM elements:

236

237

```typescript { .api }

238

interface VirtualElement {

239

getBoundingClientRect(): DOMRect;

240

contextElement?: Element;

241

}

242

```

243

244

**Virtual Element Example:**

245

246

```tsx

247

function VirtualElementExample() {

248

const [popperElement, setPopperElement] = React.useState(null);

249

250

// Create virtual element at mouse position

251

const virtualElement = React.useMemo(() => ({

252

getBoundingClientRect: () => ({

253

width: 0,

254

height: 0,

255

top: 100,

256

right: 100,

257

bottom: 100,

258

left: 100,

259

x: 100,

260

y: 100,

261

toJSON: () => ({}),

262

}),

263

}), []);

264

265

const { styles, attributes } = usePopper(virtualElement, popperElement, {

266

placement: "bottom",

267

});

268

269

return (

270

<div

271

ref={setPopperElement}

272

style={styles.popper}

273

{...attributes.popper}

274

>

275

Positioned at coordinates (100, 100)

276

</div>

277

);

278

}

279

```

280

281

## Lifecycle Management

282

283

The usePopper hook automatically manages Popper instance lifecycle:

284

285

1. **Creation**: Creates Popper instance when both elements are available

286

2. **Updates**: Updates configuration when options change

287

3. **Cleanup**: Destroys instance when elements are removed or component unmounts

288

289

## Performance Considerations

290

291

- **Memoization**: Options are memoized to prevent unnecessary recreations

292

- **Lazy initialization**: Popper instance is only created when both elements exist

293

- **Efficient updates**: Uses React's batching for optimal re-rendering

294

295

## Error Handling

296

297

The hook handles various edge cases gracefully:

298

299

- **Null elements**: Safe handling when reference or popper elements are null

300

- **Options changes**: Smooth updates when configuration changes

301

- **Cleanup**: Proper cleanup prevents memory leaks

302

303

## Best Practices

304

305

1. **Memoize complex options:**

306

```tsx

307

const options = React.useMemo(() => ({

308

placement: "top",

309

modifiers: [{ name: "offset", options: { offset: [0, 8] } }],

310

}), []);

311

```

312

313

2. **Handle loading states:**

314

```tsx

315

const { styles, attributes, state } = usePopper(ref1, ref2, options);

316

317

if (!state) {

318

return <div>Loading...</div>;

319

}

320

```

321

322

3. **Use consistent element references:**

323

```tsx

324

// ✅ Stable references

325

const [refElement, setRefElement] = useState(null);

326

const [popperElement, setPopperElement] = useState(null);

327

328

// ❌ Avoid creating new references on each render

329

const refElement = useRef(null).current;

330

```

331

332

4. **Combine with other hooks for complex behavior:**

333

```tsx

334

function useTooltip() {

335

const [referenceElement, setReferenceElement] = useState(null);

336

const [popperElement, setPopperElement] = useState(null);

337

const [visible, setVisible] = useState(false);

338

339

const popper = usePopper(referenceElement, popperElement, {

340

placement: "top",

341

});

342

343

return {

344

...popper,

345

visible,

346

setVisible,

347

setReferenceElement,

348

setPopperElement,

349

};

350

}

351

```