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.
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
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():
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:
<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 nongAfterViewInittiming concerns when read incomputed()oreffect().- In React 19 + TypeScript,
useRefrequires an initial value. UseuseRef<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
| React | Angular |
|---|---|
const ref = useRef<HTMLInputElement>(null) | el = viewChild.required<ElementRef<HTMLInputElement>>('el') |
<input ref={ref} /> | <input #el /> |
ref.current?.value | this.el().nativeElement.value |