What is the difference between useRef and createRef?

👨‍💻 Frontend Developer 🟠 May come up 🎚️ Medium
#React #Hooks

Brief Answer

useRef and createRef are two ways to create references in React with different use cases:

  1. useRef — hook for functional components 🎣
  2. createRef — method for class components 🏗️
  3. Lifetime — useRef persists between renders ♻️
  4. Performance — useRef is more optimized 🚀
  5. Usage — useRef in functions, createRef in classes 📍
// useRef in functional component
const inputRef = useRef(null);
 
// createRef in class component
this.inputRef = createRef();

Full Answer

useRef and createRef are tools for working with DOM elements and storing mutable values in React! They solve similar tasks but work differently. 🔧

Main differences

CharacteristicuseRefcreateRef
Component typeFunctionalClass
LifetimePersists between rendersCreated anew each time
PerformanceHighLower
ModernityModern approachLegacy approach

1. useRef — for functional components

useRef creates a mutable object that persists between renders:

import { useRef, useEffect } from 'react';
 
function MyComponent() {
  const inputRef = useRef(null);
  const countRef = useRef(0);
 
  useEffect(() => {
    // Focus on input
    inputRef.current.focus();
  }, []);
 
  const handleClick = () => {
    countRef.current += 1;
    console.log(countRef.current); // Doesn't trigger rerender
  };
 
  return (
    <div>
      <input ref={inputRef} />
      <button onClick={handleClick}>Increment counter</button>
    </div>
  );
}

2. createRef — for class components

createRef creates a new reference object on each call:

import React, { Component, createRef } from 'react';
 
class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.inputRef = createRef();
    this.countRef = createRef();
  }
 
  componentDidMount() {
    // Focus on input
    this.inputRef.current.focus();
  }
 
  handleClick = () => {
    console.log(this.inputRef.current.value);
  };
 
  render() {
    return (
      <div>
        <input ref={this.inputRef} />
        <button onClick={this.handleClick}>Get value</button>
      </div>
    );
  }
}

3. Lifetime and performance

useRef — optimized

function Component() {
  const ref = useRef(null); // Created once
  
  // ref.current is always the same object
  console.log(ref); // { current: null }
  
  return <div ref={ref} />;
}

createRef — created anew

class Component extends React.Component {
  render() {
    const ref = createRef(); // Created on every render!
    
    return <div ref={ref} />;
  }
}
 
// Correct usage in class
class Component extends React.Component {
  constructor(props) {
    super(props);
    this.ref = createRef(); // Created once
  }
  
  render() {
    return <div ref={this.ref} />;
  }
}

4. Practical examples

DOM element access

// useRef
function ScrollToTop() {
  const topRef = useRef(null);
  
  const scrollToTop = () => {
    topRef.current?.scrollIntoView({ behavior: 'smooth' });
  };
  
  return (
    <div>
      <div ref={topRef}>Top of page</div>
      <button onClick={scrollToTop}>Go to top</button>
    </div>
  );
}
 
// createRef
class ScrollToTop extends Component {
  constructor(props) {
    super(props);
    this.topRef = createRef();
  }
  
  scrollToTop = () => {
    this.topRef.current?.scrollIntoView({ behavior: 'smooth' });
  };
  
  render() {
    return (
      <div>
        <div ref={this.topRef}>Top of page</div>
        <button onClick={this.scrollToTop}>Go to top</button>
      </div>
    );
  }
}

Storing mutable values

// useRef for storing values without rerender
function Timer() {
  const [time, setTime] = useState(0);
  const intervalRef = useRef(null);
  
  const startTimer = () => {
    intervalRef.current = setInterval(() => {
      setTime(prev => prev + 1);
    }, 1000);
  };
  
  const stopTimer = () => {
    clearInterval(intervalRef.current);
  };
  
  return (
    <div>
      <p>Time: {time}</p>
      <button onClick={startTimer}>Start</button>
      <button onClick={stopTimer}>Stop</button>
    </div>
  );
}

5. When to use each

Use useRef when:

  • ✅ Working with functional components
  • ✅ Need access to DOM elements
  • ✅ Storing mutable values
  • ✅ Working with timers/intervals

Use createRef when:

  • ✅ Working with class components
  • ✅ Supporting legacy code
  • ✅ Need compatibility with older React versions

Best practices

  1. Prefer useRef in modern applications 🎯
  2. Initialize in constructor for createRef 🏗️
  3. Check for null before using ✅
  4. Don’t use for state — only for mutable values 🚫

Common mistakes

Wrong:

// createRef in render - created anew!
class Component extends React.Component {
  render() {
    const ref = createRef();
    return <div ref={ref} />;
  }
}

Correct:

// createRef in constructor
class Component extends React.Component {
  constructor(props) {
    super(props);
    this.ref = createRef();
  }
  
  render() {
    return <div ref={this.ref} />;
  }
}

Conclusion

Main differences between useRef and createRef:

  • useRef — modern hook for functional components
  • createRef — classic method for class components
  • Performance — useRef is more optimized
  • Lifetime — useRef persists, createRef is recreated

Use useRef in modern development! 🚀