Working with Refs & PortalsEntry 1 of 6

Refs for DOM Access (useRef)

A ref is an object created with useRef() whose .current property holds a value that persists across renders. Attached to a JSX element, .current points to the real DOM node.

react.devuseRef

Notes

Refs let you step outside React's declarative model to work with DOM elements directly. Common uses:

  • Reading an input's value without tracking every keystroke in state.
  • Managing focus, text selection, or media playback.
  • Measuring elements or calling browser APIs (scrollIntoView(), showModal()).

Connect a ref by passing it to an element's ref prop. React sets .current to the DOM node after rendering and back to null when the element is removed.

Changing a ref does not re-execute the component. If the UI must reflect a value, store it in state as well (see Entry 2).

What / Why / When

What

A direct handle to a DOM element.

Why

Some tasks (focus, measuring, reading a value once) are simpler imperatively than through state.

When

You need to read from or command a DOM element, not to render data.

Use case

Read a player's name from an input only when the user clicks "Set Name".

Code compare

React
import { useRef, useState } from "react";​export function Player() {  const playerNameRef = useRef<HTMLInputElement>(null);  const [playerName, setPlayerName] = useState<string | null>(null);​  function handleClick() {    setPlayerName(playerNameRef.current?.value ?? ""); // read the DOM value once  }​  return (    <section>      <h2>Welcome {playerName ?? "unknown entity"}</h2>      <input ref={playerNameRef} type="text" placeholder="Enter player name" />      <button onClick={handleClick}>Set Name</button>    </section>  );}

Mark the element with a template reference variable (#name) and query it with viewChild():

Angular
import { Component, ElementRef, signal, viewChild } from "@angular/core";​@Component({  selector: "app-player",  template: `    <section>      <h2>Welcome {{ playerName() ?? "unknown entity" }}</h2>      <input #playerNameInput type="text" placeholder="Enter player name" />      <button (click)="handleClick()">Set Name</button>    </section>  `,})export class PlayerComponent {  private playerNameInput = viewChild.required<ElementRef<HTMLInputElement>>("playerNameInput");  playerName = signal<string | null>(null);​  handleClick() {    this.playerName.set(this.playerNameInput().nativeElement.value);  }}

For simple cases, the template variable alone is enough, with no class code:

Angular
<input #nameInput type="text" /> <button (click)="playerName.set(nameInput.value)">Set Name</button>

Differences and gotchas

  • viewChild() (v17.2+) is the signal-based replacement for the @ViewChild() decorator. Both work; the signal version needs no ! and no ngAfterViewInit timing concerns when read in computed() or effect().
  • In React 19 + TypeScript, useRef requires an initial value. Use useRef<HTMLInputElement>(null) for DOM refs.
  • Angular template variables give direct access in the template itself. React has no equivalent; everything goes through a ref.
  • Don't use refs to change what React renders (e.g. setting innerHTML). Use them to read values or call DOM methods.

Quick reference

ReactAngular
const ref = useRef<HTMLInputElement>(null)el = viewChild.required<ElementRef<HTMLInputElement>>('el')
<input ref={ref} /><input #el />
ref.current?.valuethis.el().nativeElement.value