Quick Links

Refs are a React feature which let you directly access DOM nodes created by a component's

        render()
    

method. They provide a way to break out of React's declarative rendering so you can call browser APIs.

When working with React, you usually declare what a component's DOM should look like using JSX:

class MyComponent extends React.Component {
    

state = {value: ""};

handleChange = e => this.setState({value: e.target.value});

render() {

return (

<div>

<h1>Hello World!</h1>

<input

onChange={this.handleChange}

value={this.state.value} />

</div>

);

}

};

Internally, React transpiles the JSX to work out how to manipulate the DOM. It'll use browser functions such as

        document.createElement()
    

and

        document.appendChild()
    

to create the DOM structure you've declared.

This drastically simplifies the code you need to programmatically update the DOM. To modify elements, you change their props or update a component's state. React then calculates the differences and makes the DOM adjustments necessary.

The Case For Refs

You don't normally get to access the DOM nodes created by React. In the example above, we have no handle to the elements we've created. This becomes problematic if you want to call a DOM API which can't be accessed declaratively via React props.

Let's consider what happens if the user enters an invalid value into the text input. If they then clicked submit in a real form, it would be a good idea to show an error message and put the focus back into the text input. You can't do this without access to the input's DOM node. You need to call the

        focus()
    

method on that element.

Enter refs. Refs give you a first-class way to gain a "reference" to React's DOM nodes. You can solve the focus issue by assigning a ref to the input. Refs are objects with a

        current
    

property that contains the DOM node they refer to.

Creating a Ref

Refs are usually created explicitly by calling

        React.createRef()
    

. You then assign them to a component instance using the special

        ref
    

prop. This isn't a real prop and can't be accessed by the component.

class DemoComponent extends React.Component {
    

inputRef = React.createRef();

focusInput = () => this.inputRef?.current.focus();

render() {

return (

<div>

<input ref={this.inputRef} />

<button onClick={this.focusInput} />

</div>

);

}

}

The ref gets assigned to the instance property

        inputRef
    

. This is then handed to the

        input
    

element by setting its

        ref
    

prop. When the button is clicked, the

        focusInput()
    

method gets called. This accesses the

        current
    

property of the ref, which contains the input's actual DOM node. It can now call

        focus()
    

to focus the text field.

The

        current
    

property of refs can be

        null
    

. This occurs when the ref isn't assigned to a rendered DOM element. In this example,

        inputRef.current
    

will be

        null
    

until the

        render()
    

method is called and the input picks up the ref. For this reason, the optional chaining operator (

        ?.
    

) is used in

        focusInput()
    

to gracefully handle the scenario where the ref doesn't yet refer to anything.

Assigning Refs to React Components

The example above shows how refs work when used with plain HTML elements. You can also assign a ref to React component instances. This allows you to directly call methods defined by children you render.

class View extends React.Component {
    

state = {

error: true; // Example!

};

formRef = React.createRef();

focusForm = () => this.formRef?.current.focusInput();

submit = () => {

if (this.state.error) {

alert("There was an error; check your input.");

this.focusForm();

}

};

render() {

return (

<div>

<Form ref={this.formRef} />

<Button onClick={this.submit} />

</div>

);

}

}

class Form extends React.Component {

inputRef = React.createRef();

focusInput() {

this.inputRef.current?.focus();

}

render() {

return <input ref={this.inputRef} />

}

}

In this scenario, the

        current
    

property of the

        formRef
    

in

        View
    

doesn't refer to a DOM node. Instead, it references the

        Form
    

component instance which has been rendered.

You should take care when using this approach. Always pass data to child components via props, instead of using refs as a callback system.

Generally, a ref should be used when direct DOM interaction is unavoidable. This remains true when assigning a ref to a React component instance. You shouldn't call arbitrary component methods via a ref.

Our example meets this requirement -

        Form
    

is a presentational component, whereas

        View
    

is a complex container that could be rendering multiple forms. It needs to be able to set the focus to problematic fields, even though it's not directly rendering them. The solution is to use a component ref carefully, ensuring the usage is justified by a need to manipulate the DOM.

Refs and Functional Components

Functional components cannot receive refs. They don't have instances so there's nothing to assign the ref to. However, you can use forwarding to redirect a ref to a DOM component.

Ref forwarding is an optional feature that lets a component pass a ref it receives to one of the children it renders. To forward a ref, wrap your component with a call to React's

        forwardRef()
    

function:

const InputComponent = React.forwardRef((props, ref) => (
    

<input ref={ref} value={props.value} />

));

        forwardRef()
    

accepts a function that should return a React component. The function will be called when it needs to render and will be passed two parameters: its props and its forwarded ref.

Using Refs Within Functional Components

Although functional components can't directly receive refs, they can create them with the

        useRef()
    

hook. This is equivalent to the

        createRef()
    

method available in class components.

const InputComponent = props => {
    

const ref = useRef();

return <input ref={ref} value={props.value} />

};

Importantly,

        useRef()
    

can be used for more than just refs. Its real role is to provide a way of persisting data between calls to a functional component. It returns an object with a

        current
    

property which React then maintains and restores each time the component renders.

You can therefore use

        useRef()
    

to persist arbitrary data in functional components. Set the

        current
    

property of the returned object to the value you want to keep around.

Callback Refs

A final way to work with refs is via the "callback" pattern. With this approach, you don't need to manually call

        createRef()
    

or

        useRef()
    

. Instead, you set the

        ref
    

prop to a function that React will call during rendering. It'll pass the element's DOM node as the only argument to the function.

class CallbackRef extends React.Component {
    

render() {

return <input ref={el => this.inputRef = el} />

}

}

This is a more concise way of creating refs which are assigned to instance properties. Your function receives the DOM node directly - there's no

        .current
    

to handle, unlike

        createRef()
    

.

Conclusion

React Refs let you handle scenarios you couldn't resolve with declarative rendering alone. They're your route to direct DOM manipulation when working with forms, media playback and animations. You'll also find yourself reaching for refs if you need to integrate a third-party JavaScript library that wasn't built for React.

Although refs are flexible, you shouldn't overuse them. Before creating a ref, check your components to make sure you can't achieve your goal declaratively.

Refs are a necessary part of React but they contradict the library's principles. Too many refs can quickly make your app complicated to maintain. They let you break the top-down data flow that props usually enforce.