xiaohongshu
xiaohongshu
migrating-from-forwardref
Teaches migration from forwardRef to ref-as-prop pattern in React 19. Use when seeing forwardRef usage, upgrading React components, or when refs are mentioned. forwardRef is deprecated in React 19.
Full skill instructions
forwardRef, refs, or ref forwardingReact.forwardRefWhy the Change:
Migration Path:
forwardRef still works in React 19 (deprecated, not removed)Key Difference:
// OLD: forwardRef (deprecated)
const Button = forwardRef((props, ref) => ...);
// NEW: ref as prop (React 19)
function Button({ ref, ...props }) { ... }
</overview>
<workflow> ## Migration ProcessStep 1: Identify forwardRef Usage
Search codebase for forwardRef:
# Use Grep tool
pattern: "forwardRef"
output_mode: "files_with_matches"
Step 2: Understand Current Pattern
Before (React 18):
import { forwardRef } from 'react';
const MyButton = forwardRef((props, ref) => {
return (
<button ref={ref} className={props.className}>
{props.children}
</button>
);
});
Step 3: Convert to Ref as Prop
After (React 19):
function MyButton({ children, className, ref }) {
return (
<button ref={ref} className={className}>
{children}
</button>
);
}
Step 4: Update TypeScript Types (if applicable)
Before:
import { forwardRef } from 'react';
interface ButtonProps {
variant: 'primary' | 'secondary';
children: React.ReactNode;
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ variant, children }, ref) => {
return (
<button ref={ref} className={variant}>
{children}
</button>
);
}
);
After:
import { Ref } from 'react';
interface ButtonProps {
variant: 'primary' | 'secondary';
children: React.ReactNode;
ref?: Ref<HTMLButtonElement>;
}
function Button({ variant, children, ref }: ButtonProps) {
return (
<button ref={ref} className={variant}>
{children}
</button>
);
}
Step 5: Test Component
Verify ref forwarding still works:
function Parent() {
const buttonRef = useRef(null);
useEffect(() => {
buttonRef.current?.focus();
}, []);
return <Button ref={buttonRef}>Click me</Button>;
}
</workflow>
<conditional-workflows> ## Complex ScenariosIf component uses useImperativeHandle:
Before:
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = ''; }
}));
return <input ref={inputRef} />;
});
After:
function FancyInput({ ref }) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = ''; }
}));
return <input ref={inputRef} />;
}
If component has multiple refs:
function ComplexComponent({ ref, innerRef, ...props }) {
return (
<div ref={ref}>
<input ref={innerRef} {...props} />
</div>
);
}
If using generic components:
interface GenericProps<T> {
value: T;
ref?: Ref<HTMLDivElement>;
}
function GenericComponent<T>({ value, ref }: GenericProps<T>) {
return <div ref={ref}>{String(value)}</div>;
}
</conditional-workflows>
<progressive-disclosure> ## Reference FilesFor detailed information:
../../../research/react-19-comprehensive.md (lines 1013-1033)../../../research/react-19-comprehensive.md (lines 614-623)../../../research/react-19-comprehensive.md (lines 890-916)../../../research/react-19-comprehensive.md (lines 978-1011)Load references when specific patterns are needed. </progressive-disclosure>
<examples> ## Example 1: Simple Button MigrationBefore (React 18 with forwardRef):
import { forwardRef } from 'react';
const Button = forwardRef((props, ref) => (
<button ref={ref} {...props}>
{props.children}
</button>
));
Button.displayName = 'Button';
export default Button;
After (React 19 with ref prop):
function Button({ children, ref, ...props }) {
return (
<button ref={ref} {...props}>
{children}
</button>
);
}
export default Button;
Changes Made:
forwardRef importforwardRef wrapperref to props destructuringdisplayNameBefore:
import { forwardRef, HTMLAttributes } from 'react';
interface CardProps extends HTMLAttributes<HTMLDivElement> {
title: string;
description?: string;
variant?: 'default' | 'outlined';
}
const Card = forwardRef<HTMLDivElement, CardProps>(
({ title, description, variant = 'default', ...props }, ref) => {
return (
<div ref={ref} className={`card card-${variant}`} {...props}>
<h3>{title}</h3>
{description && <p>{description}</p>}
</div>
);
}
);
Card.displayName = 'Card';
export default Card;
After:
import { Ref, HTMLAttributes } from 'react';
interface CardProps extends HTMLAttributes<HTMLDivElement> {
title: string;
description?: string;
variant?: 'default' | 'outlined';
ref?: Ref<HTMLDivElement>;
}
function Card({
title,
description,
variant = 'default',
ref,
...props
}: CardProps) {
return (
<div ref={ref} className={`card card-${variant}`} {...props}>
<h3>{title}</h3>
{description && <p>{description}</p>}
</div>
);
}
export default Card;
Changes Made:
forwardRef to Ref typeref?: Ref<HTMLDivElement> to interfaceforwardRef wrapperref to props destructuringdisplayNameBefore:
import { forwardRef, useRef, useImperativeHandle } from 'react';
const SearchInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus() {
inputRef.current?.focus();
},
clear() {
inputRef.current.value = '';
},
getValue() {
return inputRef.current?.value || '';
}
}));
return (
<input
ref={inputRef}
type="text"
placeholder="Search..."
{...props}
/>
);
});
export default SearchInput;
After:
import { useRef, useImperativeHandle } from 'react';
function SearchInput({ ref, ...props }) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus() {
inputRef.current?.focus();
},
clear() {
inputRef.current.value = '';
},
getValue() {
return inputRef.current?.value || '';
}
}));
return (
<input
ref={inputRef}
type="text"
placeholder="Search..."
{...props}
/>
);
}
export default SearchInput;
Usage (unchanged):
function SearchBar() {
const searchRef = useRef();
const handleClear = () => {
searchRef.current?.clear();
};
return (
<>
<SearchInput ref={searchRef} />
<button onClick={handleClear}>Clear</button>
</>
);
}
Before:
import { forwardRef, ComponentPropsWithoutRef, ElementRef } from 'react';
type ButtonElement = ElementRef<'button'>;
type ButtonProps = ComponentPropsWithoutRef<'button'> & {
variant?: 'primary' | 'secondary';
};
const Button = forwardRef<ButtonElement, ButtonProps>(
({ variant = 'primary', className, ...props }, ref) => {
return (
<button
ref={ref}
className={`btn btn-${variant} ${className || ''}`}
{...props}
/>
);
}
);
Button.displayName = 'Button';
After:
import { Ref, ComponentPropsWithoutRef, ElementRef } from 'react';
type ButtonElement = ElementRef<'button'>;
type ButtonProps = ComponentPropsWithoutRef<'button'> & {
variant?: 'primary' | 'secondary';
ref?: Ref<ButtonElement>;
};
function Button({
variant = 'primary',
className,
ref,
...props
}: ButtonProps) {
return (
<button
ref={ref}
className={`btn btn-${variant} ${className || ''}`}
{...props}
/>
);
}
</examples>
<constraints> ## MUSTref to props interface when using TypeScriptRef<HTMLElement> type from React for TypeScriptforwardRef if still on React 18</constraints>
<validation> ## After MigrationVerify Ref Forwarding:
const ref = useRef(null);
<MyComponent ref={ref} />
// ref.current should be the DOM element
Check TypeScript Compilation:
npx tsc --noEmit
No errors about ref props
Test Component Behavior:
Verify Backward Compatibility:
</validation>
When migrating a component from forwardRef:
forwardRef importforwardRef wrapper functionref to props destructuringref type to TypeScript interface (if applicable)displayName if only used for forwardRef// Before
const Comp = forwardRef((props, ref) => <div ref={ref} />);
// After
function Comp({ ref }) { return <div ref={ref} />; }
// Before
const Comp = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({ method() {} }));
return <div />;
});
// After
function Comp({ ref }) {
useImperativeHandle(ref, () => ({ method() {} }));
return <div />;
}
// Before
const Comp = forwardRef<HTMLDivElement, Props>((props, ref) => ...);
// After
function Comp({ ref, ...props }: Props & { ref?: Ref<HTMLDivElement> }) { ... }
For comprehensive forwardRef migration documentation, see: research/react-19-comprehensive.md lines 978-1033.
React 19 supports cleanup functions in ref callbacks:
<div
ref={(node) => {
console.log('Connected:', node);
return () => {
console.log('Disconnected:', node);
};
}}
/>
When Cleanup Runs:
This works with both ref-as-prop and the old forwardRef pattern.
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience