or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

animation-and-transitions.mdevent-management.mdfocus-and-accessibility.mdid-and-refs.mdindex.mdlinks-and-navigation.mdmiscellaneous-utilities.mdplatform-detection.mdprops-and-events.mdscrolling-and-layout.mdshadow-dom-support.mdstate-and-effects.mdvirtual-events-and-input.md

props-and-events.mddocs/

0

# Props & Event Utilities

1

2

Intelligent prop merging, event chaining, and DOM attribute filtering for React components. These utilities handle the complex logic of combining props from multiple sources while preserving type safety.

3

4

## Capabilities

5

6

### mergeProps Function

7

8

Intelligently merges multiple props objects with special handling for event handlers, CSS classes, and IDs.

9

10

```typescript { .api }

11

/**

12

* Intelligently merges multiple props objects

13

* - Chains event handlers (functions starting with 'on[A-Z]')

14

* - Combines CSS classes using clsx

15

* - Merges IDs using mergeIds

16

* - Later props override earlier ones for other properties

17

* @param args - Multiple props objects to merge

18

* @returns Merged props object with proper TypeScript typing

19

*/

20

function mergeProps<T extends Props[]>(...args: T): UnionToIntersection<TupleTypes<T>>;

21

```

22

23

**Usage Examples:**

24

25

```typescript

26

import { mergeProps } from "@react-aria/utils";

27

28

function Button({ className, onClick, ...props }) {

29

const defaultProps = {

30

className: "btn btn-default",

31

onClick: (e) => console.log("Button clicked"),

32

type: "button" as const

33

};

34

35

const userProps = {

36

className,

37

onClick,

38

...props

39

};

40

41

// Event handlers are chained, classes combined, other props override

42

const finalProps = mergeProps(defaultProps, userProps);

43

44

return <button {...finalProps} />;

45

}

46

47

// Usage

48

<Button

49

className="btn-primary" // Results in: "btn btn-default btn-primary"

50

onClick={(e) => console.log("User click")} // Both handlers will run

51

disabled={true} // Overrides type: "button"

52

/>

53

```

54

55

### chain Function

56

57

Chains multiple callback functions to execute in sequence with the same arguments.

58

59

```typescript { .api }

60

/**

61

* Chains multiple callback functions to execute in sequence

62

* @param callbacks - Variable number of callback functions

63

* @returns Function that calls all callbacks with same arguments

64

*/

65

function chain(...callbacks: any[]): (...args: any[]) => void;

66

```

67

68

**Usage Examples:**

69

70

```typescript

71

import { chain } from "@react-aria/utils";

72

73

function MyComponent() {

74

const handleClick1 = (e) => console.log("First handler");

75

const handleClick2 = (e) => console.log("Second handler");

76

const handleClick3 = (e) => console.log("Third handler");

77

78

// Chain multiple handlers

79

const chainedHandler = chain(handleClick1, handleClick2, handleClick3);

80

81

return <button onClick={chainedHandler}>Click me</button>;

82

}

83

84

// All three handlers will execute in order when button is clicked

85

```

86

87

### filterDOMProps Function

88

89

Filters props to include only valid DOM attributes, with configurable options for different use cases.

90

91

```typescript { .api }

92

/**

93

* Filters props to include only valid DOM attributes

94

* @param props - Component props to filter

95

* @param opts - Options object controlling which props to include

96

* @returns Filtered props object safe for DOM elements

97

*/

98

function filterDOMProps(

99

props: DOMProps,

100

opts?: FilterDOMPropsOptions

101

): DOMAttributes & AriaLabelingProps & GlobalDOMAttributes;

102

103

interface FilterDOMPropsOptions {

104

/** Include ARIA labeling props (aria-label, aria-labelledby, etc.) */

105

labelable?: boolean;

106

/** Include link-specific DOM props (href, target, etc.) */

107

isLink?: boolean;

108

/** Include global DOM attributes (id, className, style, etc.) */

109

global?: boolean;

110

/** Include DOM event handlers (onClick, onFocus, etc.) */

111

events?: boolean;

112

/** Additional prop names to include */

113

propNames?: Set<string>;

114

}

115

```

116

117

**Usage Examples:**

118

119

```typescript

120

import { filterDOMProps } from "@react-aria/utils";

121

122

function MyInput({ label, onValueChange, ...props }) {

123

// Filter out non-DOM props but keep labeling props

124

const domProps = filterDOMProps(props, {

125

labelable: true,

126

events: true

127

});

128

129

return (

130

<div>

131

{label && <label>{label}</label>}

132

<input {...domProps} />

133

</div>

134

);

135

}

136

137

// Usage - onValueChange will be filtered out, but aria-label will be kept

138

<MyInput

139

aria-label="Name field"

140

onValueChange={(value) => console.log(value)} // Filtered out

141

className="input-field" // Kept if global: true

142

onClick={(e) => console.log("Clicked")} // Kept because events: true

143

customProp="value" // Filtered out

144

/>

145

```

146

147

### Advanced mergeProps Usage

148

149

Complex scenarios with multiple prop sources and type preservation:

150

151

```typescript

152

import { mergeProps, filterDOMProps } from "@react-aria/utils";

153

154

function AdvancedButton({ variant = "primary", size = "medium", ...props }) {

155

// Base props with defaults

156

const baseProps = {

157

className: `btn btn-${variant} btn-${size}`,

158

type: "button" as const

159

};

160

161

// Accessibility props

162

const a11yProps = {

163

role: "button",

164

tabIndex: 0

165

};

166

167

// User props (filtered for DOM safety)

168

const userProps = filterDOMProps(props, {

169

labelable: true,

170

events: true,

171

global: true

172

});

173

174

// Merge all props in order of precedence

175

const finalProps = mergeProps(baseProps, a11yProps, userProps);

176

177

return <button {...finalProps} />;

178

}

179

```

180

181

### Event Handler Chaining

182

183

Understanding how mergeProps chains event handlers:

184

185

```typescript

186

import { mergeProps } from "@react-aria/utils";

187

188

function EventExample() {

189

const props1 = {

190

onClick: (e) => {

191

console.log("First handler");

192

e.preventDefault(); // This will still execute

193

}

194

};

195

196

const props2 = {

197

onClick: (e) => {

198

console.log("Second handler");

199

// This runs after props1.onClick

200

}

201

};

202

203

const props3 = {

204

onClick: (e) => {

205

console.log("Third handler");

206

// This runs after props2.onClick

207

}

208

};

209

210

const mergedProps = mergeProps(props1, props2, props3);

211

212

return <button {...mergedProps}>Click me</button>;

213

// All three handlers execute in order: First, Second, Third

214

}

215

```

216

217

## Types

218

219

```typescript { .api }

220

interface DOMProps {

221

id?: string;

222

}

223

224

interface DOMAttributes extends React.DOMAttributes<HTMLElement> {

225

// Standard DOM event handlers and attributes

226

}

227

228

interface AriaLabelingProps {

229

"aria-label"?: string;

230

"aria-labelledby"?: string;

231

"aria-describedby"?: string;

232

"aria-details"?: string;

233

}

234

235

interface GlobalDOMAttributes {

236

className?: string;

237

style?: React.CSSProperties;

238

hidden?: boolean;

239

lang?: string;

240

dir?: "ltr" | "rtl";

241

// ... other global HTML attributes

242

}

243

244

type Props = Record<string, any>;

245

type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;

246

type TupleTypes<T> = { [P in keyof T]: T[P] };

247

```