VOOZH about

URL: https://blog.logrocket.com/react-reference-guide-refs-dom/

⇱ React Reference Guide: Refs and the DOM - LogRocket Blog


2020-09-04
2021
#react#reference guide
Ohans Emmanuel
26331
👁 Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out


The standard way for a parent component to interact with its child elements is via props, e.g., to modify a child, you’d re-render it with new props. That’s not news.

👁 React Reference Guide: Refs and the DOM

However, what happens when you need to access DOM nodes or React elements created in the render method of your components? Props don’t exactly help you out here.

Well, this is where refs come in handy.

Jump ahead:


🚀 Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

When to use refs

The best use cases for refs arise when you’re trying to perform imperative actions such as:

  • Managing focus, text selection, or media playback
  • Working with imperative animation
  • Integrating with third-party DOM libraries

To build more consistent React apps, you should avoid using refs for anything that can be done declaratively, i.e., via the standard React data flow with state and props. For example, instead of exposing open() and close() methods on a Modal component, pass an isOpen prop and have that managed internally via React state.


Don’t overuse refs

When you first encounter refs or are new to React, you may fall into the trap of thinking refs are an easy way to “get things done” in your app. They certainly fit the imperative model you may be used to working with.

If you find yourself thinking this, take a moment to re-evaluate how such data flow could be resolved via the typical React data flow i.e via state and props.

Oftentimes, the problem at hand may be solved by just lifting state to a parent component higher in the component tree and passing that state value down to the needed component/element.

Check out our tutorial on how to lift a component’s state using React Suspense.

N.B., the next sections show examples of how to work with Refs. These examples use React.createRef, the API introduced in React 16.3. If you’re using an earlier version of React, see the section below on using callback refs.


Creating refs

There are two steps to creating ref objects.

1.) Create a ref object using React.createRef:

class App extends React.Component {
 // see this 
 this.myRef = React.createRef()
}

You create a ref by invoking the createRef object and typically assign it to an instance property, e.g., this.myRef as seen in the example above. This is done so that the ref can be referenced throughout the component.


Over 200k developers use LogRocket to create better digital experiences

👁 Image
Learn more →

2.) Reference the created ref in the render method:

class App extends React.Component {
 myRef = React.createRef()
 render() {
 //look here
 return <div ref={this.myRef}>I am a div</div>
 }
}

After creating the ref object, you pass it on to the required element via the special ref attribute.


Accessing refs

After passing a ref to an element in the component render method, a reference to the DOM node becomes accessible, as seen below:

const domNode = this.myRef.current

Note that the current property is referenced on the created ref object, where this.myRef represents the ref passed to a DOM node via the ref attribute.

Look at the code block above. What value does the variable domNode contain? Well, that depends on the type of node the ref attribute is passed to — it’s always different.

Here are the different options:

1.) When the ref attribute is passed to an HTML element, the ref object receives the underlying DOM element as its current property.

//render 
<div ref={this.myRef} />

//node contains HTMLElement (div) 
const node = this.myRef.current

2.) When the ref attribute is passed to a custom class component, the ref object receives the mounted instance of the component.

//render 
<MyClassComponent ref={this.myRef} />

//node contains MyClassComponent class instance
const node = this.myRef.current

3.) Refs can’t be passed to a function component because they don’t have instances.

//render: don't do this. 
<MyFunctionalComponent ref={this.myRef}

The next sections will demonstrate examples of the aforementioned node types.

Adding a ref to a DOM element

Consider an example of focusing a text input on clicking a button:

👁 Focusing a Text Input on Clicking a Button

To do this, create a ref object that holds the text input DOM node and access the DOM API to focus the input, as show below:

class App extends React.Component {
 // create a ref to hold the textInput DOM element
 textInput = React.createRef();

 focusTextInput = () => {
 // get the input dom node from the ref object
 const inputDOMNode = this.textInput.current;
 // Use the browser imperative api: call the focus method
 inputDOMNode.focus();
 };

 render() {
 return (
 <div>
 {/* pass a ref attribute to the input element */}
 <input type="text" ref={this.textInput} />
 <input
 type="button"
 value="Focus input"
 onClick={this.focusTextInput}
 />
 </div>
 );
 }
}

Note that when the component mounts, React assigns the current property of the ref object with the input DOM element. When the component is unmounted, this is assigned to null.

The ref updates are also guaranteed to happen before the componentDidMount or componentDidUpdate lifecycle methods. See a demo below.

Adding a ref to a class component

What if we decided to refactor the previous example to pass a ref attribute to a class component, e.g., MyTextInput? How much changes?

Take a look below:

class CustomTextInput extends React.Component {
 myInputRef = React.createRef();
 focusTextInput = () => {
 // get dom node from ref props
 const inputDOMNode = this.myInputRef.current;
 inputDOMNode.focus();
 };

 render() {
 // pass ref on from props
 return <input type="text" ref={this.myInputRef} />;
 }
}

We have a class component, CustomTextInput, with a focusTextInput function. Note that the CustomTextInput component doesn’t invoke this function but creates and accesses the text input DOM node.

So, if you wanted to trigger this function handler from another component, e.g., to focus the input on mount or when a button is clicked, the following works perfectly:

class App extends React.Component {
 // create a ref to hold the textInput DOM element
 textInputClassInstance = React.createRef();

 focus = () => {
 // call the child handler
 // this is possible because the ref object received the child class instance
 this.textInputClassInstance.current.focusTextInput();
 };

 componentDidMount() {
 this.focus();
 }

 render() {
 return (
 <div>
 {/* pass a ref attribute to the class component. Ref receives the class instance */}
 <CustomTextInput ref={this.textInputClassInstance} />
 <input type="button" value="Focus input" onClick={this.focus} />
 </div>
 );
 }
}

See a demo below:

Refs and function components

Remember that you should not use the ref attribute on function components because they don’t have instances:

class Parent extends React.Component {
 textInput = React.createRef();

 render() {
 // This will NOT work!
 return (
 <MyFunctionComponent ref={this.textInput} />
 );
 }
}

If you want to pass a ref to a function component, consider using forwardRef, perhaps in conjunction with useImperativeHandle. You could also consider converting said component to a class component.

See useImperativeHandle in action in our React Hooks reference guide.

As stated earlier, you shouldn’t pass a ref to a functional component, but you can create and use refs within functional components, as seen below:

function CustomTextInput(props) {
 // create ref object using useRef
 const textInput = useRef(null);

 function handleClick() {
 textInput.current.focus();
 }

 return (
 <div>
 <input
 type="text"
 ref={textInput} />
 <input
 type="button"
 value="Focus the text input"
 onClick={handleClick}
 />
 </div>
 );
}

Exposing DOM refs to parent components

Occasionally, you may want to access a child’s DOM node from a parent component. This is far from ideal as it breaks component encapsulation. However, there are legitimate use cases that could warrant this technique, e.g., triggering focus or measuring the size or position of a child DOM node.

So, how should you approach this problem?

Firstly, you could add a ref to the child component, as we did in an earlier example. This is not a perfect solution since you get a component instance and not the child DOM node. Also, this doesn’t work with functional components.

With React 16.3 or higher, consider using ref forwarding for such cases. Ref forwarding allows you to expose a child’s DOM node to a parent component.

Ready to go deeper? Read our advanced tutorial on forwardRef.

With React 16.2 or lower, ref forwarding isn’t supported. So what do you do? Consider explicitly passing a ref as a differently named prop.

function CustomTextInput(props) {
 return (
 <div>
 {/* Pass ref to input node*/}
 <input ref={props.inputRef} />
 </div>
 );
}

class Parent extends React.Component {
 // create ref object 
 inputElement = React.createRef();

 render() {
 return (
 {/* pass ref object as a differently named prop: inputRef*/}
 <CustomTextInput inputRef={this.inputElement} />
 );
 }
}

Note that the approach above requires some code to be added to the child component. In more stringent use cases, you may have no control over the child component implementation. Your best bet is to use findDOMNode(), but note that this is discouraged and deprecated in StrictMode.



It is highly recommended to avoid exposing DOM nodes if you don’t need do.


Callback refs

In all the examples we’ve discussed, the ref object has been directly passed on via the ref attribute. However, React supports another way to set refs; these are called callback refs.

Instead of passing a ref object via the ref attribute:

myRefObject = React.createRef()
...
<div ref={myRefObject} />

Pass a function:

myRefCallback = (node) => {}
...
<div ref={myRefCallback} />

React will pass the component instance or DOM element as an argument to the function. This can then be stored and accessed elsewhere!

Callback refs give you a lot more control over actions to be performed when refs are set and unset. Remember that the ref callback is called with the DOM element when the component mounts, and it is called with null when it unmounts.

Consider the example below, which creates a ref callback and stores a DOM node in an instance property — a commonly implemented pattern.

class App extends React.Component {
 //create instance variable
 textInput = null;

 setTextInputRef = (element) => {
 // save DOM element received in the instance variable: textInput
 this.textInput = element;
 };

 focusTextInput = () => {
 // Focus the text input using the raw DOM API
 if (this.textInput) this.textInput.focus();
 };

 componentDidMount() {
 // autofocus the input on mount
 this.focusTextInput();
 }

 render() {
 // Use the `ref` callback to store a reference to the text input DOM
 return (
 <div>
 <input type="text" ref={this.setTextInputRef} />
 <input
 type="button"
 value="Focus input"
 onClick={this.focusTextInput}
 />
 </div>
 );
 }
}

See a demo below:

As with object refs, you can pass callback refs between components, e.g., parent and child. See the example below:

function CustomTextInput(props) {
 return (
 <div>
 {/*child component passes ref to callback*/}
 <input ref={props.inputRef} />
 </div>
 );
}

class Parent extends React.Component {
 render() {
 return (
 {/* pass a callback ref to the child component*/}
 <CustomTextInput
 inputRef={el => this.inputElement = el}
 />
 );
 }
}

In the example above, the Parent component holds the DOM value corresponding to the child text input in the instance variable inputElement.

Note that the ref callback is passed to the child component, CustomTextInput via a differently named prop: inputRef. CustomTextInput then passes the callback on to the ref attribute set on the <input> element.

Caveats with callback refs

If you define a ref callback as an inline function, a new instance of the function will be created with each render. The callback is then called twice: first will null, and then again with the DOM element. Essentially, React has to clear the old ref and set up a new one.

This shouldn’t matter so much in most cases, but to avoid this, define the ref method as a bound method on the class (for class components).


Legacy API: String refs

It is worth mentioning that an older ref API supported ref attributes as plain strings, e.g., myTextInput and the DOM node accessed as this.refs.myTextInput.

Don’t do this anymore. String refs are now considered legacy, they have some issues, and they are likely going to be removed in a future release of React.

Get set up with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side

    $ npm i --save logrocket 
    
    // Code:
    
    import LogRocket from 'logrocket'; 
    LogRocket.init('app/id');
     
    // Add to your HTML:
    
    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
     
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now
👁 Image
👁 Image
👁 Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

Debug Next.js apps with AI agents and next-browser

Learn how next-browser gives AI agents runtime context for debugging Next.js apps, including React props, hydration, PPR, forms, and performance.

👁 Image
Emmanuel John
Jun 17, 2026 ⋅ 9 min read

Stop hardcoding LLM SDKs: Dynamic LLM routing with OpenRouter and Next.js

Build dynamic LLM routing in Next.js with OpenRouter, TanStack AI, task classification, model fallbacks, and cost-aware routing.

👁 Image
Chizaram Ken
Jun 16, 2026 ⋅ 13 min read

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension — no new framework required.

👁 Image
Ikeh Akinyemi
Jun 12, 2026 ⋅ 6 min read

How to add authentication to a React Native app with Better Auth

Learn how to build a full React Native auth system using Better Auth and Expo — with email/password login, Google OAuth, session persistence, and protected routes.

👁 Image
Chinwike Maduabuchi
Jun 9, 2026 ⋅ 13 min read
View all posts

Would you be interested in joining LogRocket's developer community?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now