Skip to content

Commit 4fbb745

Browse files
Update forwardRef documentation to lead with modern React 19
1 parent 0f2f10f commit 4fbb745

File tree

2 files changed

+96
-42
lines changed

2 files changed

+96
-42
lines changed

docs/basic/getting-started/forward-create-ref.md

Lines changed: 93 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,62 @@ id: forward_and_create_ref
33
title: forwardRef/createRef
44
---
55

6-
Check the [Hooks section](https://github.com/typescript-cheatsheets/react/blob/main/README.md#hooks) for `useRef`.
6+
For `useRef`, check the [Hooks section](/docs/basic/getting-started/hooks#useref).
77

8-
`createRef`:
8+
## Ref as a Prop (Recommended for React 19+)
9+
10+
In React 19+, you can access `ref` directly as a prop in function components - no `forwardRef` wrapper needed.
11+
12+
### Option 1: Inherit all props from a native element
13+
14+
Use `ComponentPropsWithRef` to inherit all props from a native element.
915

1016
```tsx
11-
import { createRef, PureComponent } from "react";
17+
import { ComponentPropsWithRef, useRef } from "react";
1218

13-
class CssThemeProvider extends PureComponent<Props> {
14-
private rootRef = createRef<HTMLDivElement>(); // like this
15-
render() {
16-
return <div ref={this.rootRef}>{this.props.children}</div>;
17-
}
19+
function MyInput(props: ComponentPropsWithRef<"input">) {
20+
return <input {...props} />;
21+
}
22+
23+
// Usage in parent component
24+
function Parent() {
25+
const inputRef = useRef<HTMLInputElement>(null);
26+
27+
return <MyInput ref={inputRef} placeholder="Type here..." />;
28+
}
29+
```
30+
31+
### Option 2: Explicit typing
32+
33+
If you have custom props and want fine-grained control, you can explicitly type the ref:
34+
35+
```tsx
36+
import { Ref, useRef } from "react";
37+
38+
interface MyInputProps {
39+
placeholder: string;
40+
ref: Ref<HTMLInputElement>;
41+
}
42+
43+
function MyInput(props: MyInputProps) {
44+
return <input {...props} />;
45+
}
46+
47+
// Usage in parent component
48+
function Parent() {
49+
const inputRef = useRef<HTMLInputElement>(null);
50+
51+
return <MyInput ref={inputRef} placeholder="Type here..." />;
1852
}
1953
```
2054

21-
`forwardRef`:
55+
**Read more**: [Wrapping/Mirroring a HTML Element](/docs/advanced/patterns_by_usecase#wrappingmirroring-a-html-element)
56+
57+
## Legacy Approaches (Pre-React 19)
58+
59+
### forwardRef
60+
61+
For React 18 and earlier, use `forwardRef`:
2262

2363
```tsx
2464
import { forwardRef, ReactNode } from "react";
@@ -52,7 +92,7 @@ interface Props {
5292
export const FancyButton = forwardRef(
5393
(
5494
props: Props,
55-
ref: Ref<HTMLButtonElement> // <-- here!
95+
ref: Ref<HTMLButtonElement> // <-- explicit immutable ref type
5696
) => (
5797
<button ref={ref} className="MyClassName" type={props.type}>
5898
{props.children}
@@ -63,42 +103,47 @@ export const FancyButton = forwardRef(
63103

64104
</details>
65105

66-
If you are grabbing the props of a component that forwards refs, use [`ComponentPropsWithRef`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L770).
106+
If you need to grab props from a component that forwards refs, use [`ComponentPropsWithRef`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L770).
67107

68-
`ref` as a prop:
108+
### createRef
69109

70-
React 19, you can access ref as prop for function components.
110+
`createRef` is mostly used for class components. Function components typically rely on `useRef` instead.
71111

72112
```tsx
73-
function MyInput({ placeholder, ref }) {
74-
return <input placeholder={placeholder} ref={ref} />;
75-
}
113+
import { createRef, PureComponent } from "react";
76114

77-
// In parent
78-
<MyInput ref={ref} />;
115+
class CssThemeProvider extends PureComponent<Props> {
116+
private rootRef = createRef<HTMLDivElement>();
117+
118+
render() {
119+
return <div ref={this.rootRef}>{this.props.children}</div>;
120+
}
121+
}
79122
```
80123

81-
Read more [`ref` as a prop](https://react.dev/blog/2024/12/05/react-19#ref-as-a-prop).
124+
## Generic Components with Refs
125+
126+
Generic components typically require manual ref handling since their generic nature prevents automatic type inference. Here are the main approaches:
82127

83-
## Generic forwardRefs
128+
Read more context in [this article](https://fettblog.eu/typescript-react-generic-forward-refs/).
84129

85-
Read more context in https://fettblog.eu/typescript-react-generic-forward-refs/:
130+
### Option 1: Wrapper Component
86131

87-
### Option 1 - Wrapper component
132+
The most straightforward approach is to manually handle refs through props:
88133

89-
```ts
90-
type ClickableListProps<T> = {
134+
```tsx
135+
interface ClickableListProps<T> {
91136
items: T[];
92137
onSelect: (item: T) => void;
93138
mRef?: React.Ref<HTMLUListElement> | null;
94-
};
139+
}
95140

96141
export function ClickableList<T>(props: ClickableListProps<T>) {
97142
return (
98143
<ul ref={props.mRef}>
99144
{props.items.map((item, i) => (
100145
<li key={i}>
101-
<button onClick={(el) => props.onSelect(item)}>Select</button>
146+
<button onClick={() => props.onSelect(item)}>Select</button>
102147
{item}
103148
</li>
104149
))}
@@ -107,17 +152,19 @@ export function ClickableList<T>(props: ClickableListProps<T>) {
107152
}
108153
```
109154

110-
### Option 2 - Redeclare forwardRef
155+
### Option 2: Redeclare forwardRef
156+
157+
For true `forwardRef` behavior with generics, extend the module declaration:
111158

112-
```ts
113-
// Redeclare forwardRef
159+
```tsx
160+
// Redeclare forwardRef to support generics
114161
declare module "react" {
115162
function forwardRef<T, P = {}>(
116163
render: (props: P, ref: React.Ref<T>) => React.ReactElement | null
117164
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
118165
}
119166

120-
// Just write your components like you're used to!
167+
// Now you can use forwardRef with generics normally
121168
import { forwardRef, ForwardedRef } from "react";
122169

123170
interface ClickableListProps<T> {
@@ -133,7 +180,7 @@ function ClickableListInner<T>(
133180
<ul ref={ref}>
134181
{props.items.map((item, i) => (
135182
<li key={i}>
136-
<button onClick={(el) => props.onSelect(item)}>Select</button>
183+
<button onClick={() => props.onSelect(item)}>Select</button>
137184
{item}
138185
</li>
139186
))}
@@ -144,10 +191,12 @@ function ClickableListInner<T>(
144191
export const ClickableList = forwardRef(ClickableListInner);
145192
```
146193

147-
### Option 3 - Call signature
194+
### Option 3: Call Signature
195+
196+
If you need both generic support and proper forwardRef behavior with full type inference, you can use the call signature:
148197

149-
```ts
150-
// Add to `index.d.ts`
198+
```tsx
199+
// Add to your type definitions (e.g. in `index.d.ts` file)
151200
interface ForwardRefWithGenerics extends React.FC<WithForwardRefProps<Option>> {
152201
<T extends Option>(props: WithForwardRefProps<T>): ReturnType<
153202
React.FC<WithForwardRefProps<T>>
@@ -158,12 +207,17 @@ export const ClickableListWithForwardRef: ForwardRefWithGenerics =
158207
forwardRef(ClickableList);
159208
```
160209

161-
Credits: https://stackoverflow.com/a/73795494
210+
Credits: [https://stackoverflow.com/a/73795494](https://stackoverflow.com/a/73795494)
162211

163-
## More Info
212+
:::note
213+
Option 1 is usually sufficient and clearer. Use Option 2 when you specifically need `forwardRef` behavior. Use Option 3 for advanced library scenarios requiring both generics and full forwardRef type inference.
214+
:::
164215

165-
- https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315
216+
## Additional Resources
166217

167-
You may also wish to do [Conditional Rendering with `forwardRef`](https://github.com/typescript-cheatsheets/react/issues/167).
218+
- [React refs with TypeScript](https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315)
219+
- [Conditional rendering with forwardRef](https://github.com/typescript-cheatsheets/react/issues/167)
220+
221+
---
168222

169-
[Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new).
223+
[Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new)

docs/react-types/ComponentProps.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
title: ComponentProps<ElementType>
33
---
44

5-
`ComponentProps<ElementType>` constructs a type with all valid props of an element or inferred props of a component.
5+
`ComponentProps<ElementType>` constructs a type with all valid props of an element or inferred props of a component. It's an alias for `ComponentPropsWithRef<ElementType>`.
66

77
:::note
8+
**React 19+**: `ComponentPropsWithRef<ElementType>` is recommended as refs are now passed as props in function components. (See [forwardRef/createRef](/docs/basic/getting-started/forward_and_create_ref))
89

9-
Prefer `ComponentPropsWithRef<ElementType>` if ref is forwarded and `ComponentPropsWithoutRef<ElementType>` when ref is not forwarded.
10-
10+
**React ≤18**: Prefer `ComponentPropsWithRef<ElementType>` if ref is forwarded and `ComponentPropsWithoutRef<ElementType>` when ref is not forwarded.
1111
:::
1212

1313
## Parameters

0 commit comments

Comments
 (0)