v19.2Latest

Manipulating the DOM with Refs

React automatically updates the DOM to match your render output, so your components won’t often need to manipulate it. However, sometimes you might need access to the DOM elements managed by React—for example, to focus a node, scroll to it, or measure its size and position. There is no built-in way to do those things in React, so you will need a ref to the DOM node.

You will learn
  • How to access a DOM node managed by React with the ref attribute
  • How the ref JSX attribute relates to the useRef Hook
  • How to access another component’s DOM node
  • In which cases it’s safe to modify the DOM managed by React

Getting a ref to the node

To access a DOM node managed by React, first, import the useRef Hook:

Then, use it to declare a ref inside your component:

Finally, pass your ref as the ref attribute to the JSX tag for which you want to get the DOM node:

The useRef Hook returns an object with a single property called current. Initially, myRef.current will be null. When React creates a DOM node for this <div>, React will put a reference to this node into myRef.current. You can then access this DOM node from your event handlers and use the built-in browser APIs defined on it.

Example: Focusing a text input

In this example, clicking the button will focus the input:

To implement this:

  1. Declare inputRef with the useRef Hook.
  2. Pass it as <input ref={inputRef}>. This tells React to put this <input>’s DOM node into inputRef.current.
  3. In the handleClick function, read the input DOM node from inputRef.current and call focus() on it with inputRef.current.focus().
  4. Pass the handleClick event handler to <button> with onClick.

While DOM manipulation is the most common use case for refs, the useRef Hook can be used for storing other things outside React, like timer IDs. Similarly to state, refs remain between renders. Refs are like state variables that don’t trigger re-renders when you set them. Read about refs in Referencing Values with Refs.

Example: Scrolling to an element

You can have more than a single ref in a component. In this example, there is a carousel of three images. Each button centers an image by calling the browser scrollIntoView() method on the corresponding DOM node:

Accessing another component’s DOM nodes

Pitfall

Refs are an escape hatch. Manually manipulating another component’s DOM nodes can make your code fragile.

You can pass refs from parent component to child components just like any other prop.

In the above example, a ref is created in the parent component, MyForm, and is passed to the child component, MyInput. MyInput then passes the ref to <input>. Because <input> is a built-in component React sets the .current property of the ref to the <input> DOM element.

The inputRef created in MyForm now points to the <input> DOM element returned by MyInput. A click handler created in MyForm can access inputRef and call focus() to set the focus on <input>.

When React attaches the refs

In React, every update is split in two phases:

  • During render, React calls your components to figure out what should be on the screen.
  • During commit, React applies changes to the DOM.

In general, you don’t want to access refs during rendering. That goes for refs holding DOM nodes as well. During the first render, the DOM nodes have not yet been created, so ref.current will be null. And during the rendering of updates, the DOM nodes haven’t been updated yet. So it’s too early to read them.

React sets ref.current during the commit. Before updating the DOM, React sets the affected ref.current values to null. After updating the DOM, React immediately sets them to the corresponding DOM nodes.

Usually, you will access refs from event handlers. If you want to do something with a ref, but there is no particular event to do it in, you might need an Effect. We will discuss Effects on the next pages.

Best practices for DOM manipulation with refs

Refs are an escape hatch. You should only use them when you have to “step outside React”. Common examples of this include managing focus, scroll position, or calling browser APIs that React does not expose.

If you stick to non-destructive actions like focusing and scrolling, you shouldn’t encounter any problems. However, if you try to modify the DOM manually, you can risk conflicting with the changes React is making.

To illustrate this problem, this example includes a welcome message and two buttons. The first button toggles its presence using conditional rendering and state, as you would usually do in React. The second button uses the remove() DOM API to forcefully remove it from the DOM outside of React’s control.

Try pressing “Toggle with setState” a few times. The message should disappear and appear again. Then press “Remove from the DOM”. This will forcefully remove it. Finally, press “Toggle with setState”:

After you’ve manually removed the DOM element, trying to use setState to show it again will lead to a crash. This is because you’ve changed the DOM, and React doesn’t know how to continue managing it correctly.

Avoid changing DOM nodes managed by React. Modifying, adding children to, or removing children from elements that are managed by React can lead to inconsistent visual results or crashes like above.

However, this doesn’t mean that you can’t do it at all. It requires caution. You can safely modify parts of the DOM that React has no reason to update. For example, if some <div> is always empty in the JSX, React won’t have a reason to touch its children list. Therefore, it is safe to manually add or remove elements there.

Recap

  • Refs are a generic concept, but most often you’ll use them to hold DOM elements.
  • You instruct React to put a DOM node into myRef.current by passing <div ref={myRef}>.
  • Usually, you will use refs for non-destructive actions like focusing, scrolling, or measuring DOM elements.
  • A component doesn’t expose its DOM nodes by default. You can opt into exposing a DOM node by using the ref prop.
  • Avoid changing DOM nodes managed by React.
  • If you do modify DOM nodes managed by React, modify parts that React has no reason to update.

Try out some challenges

Challenge 1 of 4:Play and pause the video #

In this example, the button toggles a state variable to switch between a playing and a paused state. However, in order to actually play or pause the video, toggling state is not enough. You also need to call play() and pause() on the DOM element for the <video>. Add a ref to it, and make the button work.

For an extra challenge, keep the “Play” button in sync with whether the video is playing even if the user right-clicks the video and plays it using the built-in browser media controls. You might want to listen to onPlay and onPause on the video to do that.