Background parttern

Difference between createRef, useRef and forwardRef

Difference between createRef, useRef and forwardRef (use cases and examples)

reactcreateRefuseRefforwardRefDOM

Refs are in react are used to create references to certain values and nodes in the react DOM.

Refs in React provide a way to access DOM nodes or React elements created in the render method. While they might seem similar at first glance, createRef, useRef, and forwardRef serve distinct purposes and are used in different scenarios.

## Comparison Table

FeaturecreateRefuseRefforwardRef
TypeFunction (for class components)Hook (for functional components)HOC (Higher-Order Component)
PurposeCreate a ref for class componentsCreate a mutable ref object for functional componentsPass a ref from a parent to a child functional component
PersistenceNew ref on every renderPersists across rendersParent's ref is attached to a child's DOM node
Usagethis.myRef = createRef();const myRef = useRef(initialValue);const MyComponent = forwardRef((props, ref) => ...)
Mutatingthis.myRef.currentmyRef.currentref is passed and attached to a DOM node
When to UseLegacy class componentsFunctional components and stable referencesParent needs direct access to child DOM node

## createRef (Functional Equivalent with useRef)

Although createRef is traditionally used in class components, its functional counterpart is useRef, which serves the same purpose in functional components.

### Example: using useRef for DOM access

import React, { useRef } from 'react';

export function CreateRefFunctionalExample() {
  const myInputRef = useRef<HTMLInputElement>(null);

  const focusInput = () => {
    if (myInputRef.current) {
      myInputRef.current.focus();
    }
  };

  return (
    <div>
      <h3>`createRef` Equivalent with `useRef` (Functional)</h3>
      <input type="text" ref={myInputRef} />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}
import React, { useRef } from "react";
export function CreateRefFunctionalExample() {
	const myInputRef = useRef(null);
	const focusInput = () => {
		if (myInputRef.current) {
			myInputRef.current.focus();
		}
	};
	return <div>
      <h3>`createRef` Equivalent with `useRef` (Functional)</h3>
      <input type="text" ref={myInputRef} />
      <button onClick={focusInput}>Focus Input</button>
    </div>;
}

## useRef

useRef is a Hook that returns a mutable ref object persisting across renders. It's the go-to ref tool for functional components.

### Example 1: Accessing a DOM Element

import React, { useRef } from 'react';

export function ComponentWithRef() {
  const myInputRef = useRef<HTMLInputElement>(null);

  const focusInput = () => {
    if (myInputRef.current) {
      myInputRef.current.focus();
    }
  };

  return (
    <div>
      <h3>`useRef` (Accessing DOM Element)</h3>
      <input type="text" ref={myInputRef} />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}
import React, { useRef } from "react";
export function ComponentWithRef() {
	const myInputRef = useRef(null);
	const focusInput = () => {
		if (myInputRef.current) {
			myInputRef.current.focus();
		}
	};
	return <div>
      <h3>`useRef` (Accessing DOM Element)</h3>
      <input type="text" ref={myInputRef} />
      <button onClick={focusInput}>Focus Input</button>
    </div>;
}

### Example 2: Storing Mutable Value

import React, { useRef } from 'react';

export function CounterWithoutRerender() {
  const countRef = useRef<number>(0);

  const increment = () => {
    countRef.current++;
    console.log('Count (from ref):', countRef.current);
  };

  return (
    <div>
      <h3>`useRef` (Storing Mutable Value)</h3>
      <p>Count (from ref - check console): {countRef.current}</p>
      <button onClick={increment}>Increment Count</button>
      <p className="text-sm text-gray-500">
        (Note: The displayed count above won't update until a re-render is triggered by something else.)
      </p>
    </div>
  );
}
import React, { useRef } from "react";
export function CounterWithoutRerender() {
	const countRef = useRef(0);
	const increment = () => {
		countRef.current++;
		console.log("Count (from ref):", countRef.current);
	};
	return <div>
      <h3>`useRef` (Storing Mutable Value)</h3>
      <p>Count (from ref - check console): {countRef.current}</p>
      <button onClick={increment}>Increment Count</button>
      <p className="text-sm text-gray-500">
        (Note: The displayed count above won't update until a re-render is triggered by something else.)
      </p>
    </div>;
}

## forwardRef

forwardRef is a Higher-Order Component that allows a parent component to pass a ref down to a child, which can attach it to a DOM node.

💡 Without forwardRef, passing a ref to a functional component will result in an error.

### Example: using forwardRef on components

button.tsx
import { forwardRef } from 'react';

interface MyButtonProps {
  className?: string;
}

const MyButton = forwardRef<HTMLButtonElement, MyButtonProps>(({ children, className }, ref) => {
  return (
    <button
      ref={ref}
      className={`rounded-md bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 ${className}`}
      onClick={onClick}
    >
      {children}
    </button>
  );
});
MyButton.displayName = 'Button';

export { MyButton };
button.tsx
import { forwardRef } from "react";
const MyButton = forwardRef(({ children, className }, ref) => {
	return <button ref={ref} className={`rounded-md bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 ${className}`} onClick={onClick}>
      {children}
    </button>;
});
MyButton.displayName = "Button";
export { MyButton };

Using the button Component

App.tsx
import React, { useRef } from 'react';
import MyButton from './button';

export default function AppWithForwardRef() {
  const buttonRef = useRef<HTMLButtonElement>(null);

  const handleFocusButtonClick = () => {
    if (buttonRef.current) {
      buttonRef.current.focus();
    }
  };

  return (
    <div>
      <h3>`forwardRef` Example</h3>
      <MyButton ref={buttonRef} onClick={() => console.log('Button clicked!')}>
        Click Me
      </MyButton>
      <button
        onClick={handleFocusButtonClick}
        className="ml-4 rounded-md bg-green-500 px-4 py-2 text-white hover:bg-green-600"
      >
        Focus the MyButton
      </button>
    </div>
  );
}
App.tsx
import React, { useRef } from "react";
import MyButton from "./button";
export default function AppWithForwardRef() {
	const buttonRef = useRef(null);
	const handleFocusButtonClick = () => {
		if (buttonRef.current) {
			buttonRef.current.focus();
		}
	};
	return <div>
      <h3>`forwardRef` Example</h3>
      <MyButton ref={buttonRef} onClick={() => console.log("Button clicked!")}>
        Click Me
      </MyButton>
      <button onClick={handleFocusButtonClick} className="ml-4 rounded-md bg-green-500 px-4 py-2 text-white hover:bg-green-600">
        Focus the MyButton
      </button>
    </div>;
}

## Conclusion

USE THISWHEN
createRef (via useRef in functional components)For accessing DOM or component refs in functional components.
useRefFor mutable values or DOM references that persist across renders.
forwardRefWhen a parent needs access to a DOM element in a child functional component.

Published on May 31, 2024

4 min read

Found an Issue!

Find an issue with this post? Think you could clarify, update or add something? All my posts are available to edit on Github. Any fix, little or small, is appreciated!

Edit on GitHub