---
title: refs
---
Validates correct usage of refs, not reading/writing during render. See the "pitfalls" section in [`useRef()` usage](/reference/react/useRef#usage).
## Rule Details {/*rule-details*/}
Refs hold values that aren't used for rendering. Unlike state, changing a ref doesn't trigger a re-render. Reading or writing `ref.current` during render breaks React's expectations. Refs might not be initialized when you try to read them, and their values can be stale or inconsistent.
## How It Detects Refs {/*how-it-detects-refs*/}
The lint only applies these rules to values it knows are refs. A value is inferred as a ref when the compiler sees any of the following patterns:
- Returned from `useRef()` or `React.createRef()`.
```js
const scrollRef = useRef(null);
```
- An identifier named `ref` or ending in `Ref` that reads from or writes to `.current`.
```js
buttonRef.current = node;
```
- Passed through a JSX `ref` prop (for example `
`).
```jsx
```
Once something is marked as a ref, that inference follows the value through assignments, destructuring, or helper calls. This lets the lint surface violations even when `ref.current` is accessed inside another function that received the ref as an argument.
## Common Violations {/*common-violations*/}
- Reading `ref.current` during render
- Updating `refs` during render
- Using `refs` for values that should be state
### Invalid {/*invalid*/}
Examples of incorrect code for this rule:
```js
// ❌ Reading ref during render
function Component() {
const ref = useRef(0);
const value = ref.current; // Don't read during render
return
{value}
;
}
// ❌ Modifying ref during render
function Component({value}) {
const ref = useRef(null);
ref.current = value; // Don't modify during render
return ;
}
```
### Valid {/*valid*/}
Examples of correct code for this rule:
```js
// ✅ Read ref in effects/handlers
function Component() {
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
console.log(ref.current.offsetWidth); // OK in effect
}
});
return ;
}
// ✅ Use state for UI values
function Component() {
const [count, setCount] = useState(0);
return (
);
}
// ✅ Lazy initialization of ref value
function Component() {
const ref = useRef(null);
// Initialize only once on first use
if (ref.current === null) {
ref.current = expensiveComputation(); // OK - lazy initialization
}
const handleClick = () => {
console.log(ref.current); // Use the initialized value
};
return ;
}
```
## Troubleshooting {/*troubleshooting*/}
### The lint flagged my plain object with `.current` {/*plain-object-current*/}
The name heuristic intentionally treats `ref.current` and `fooRef.current` as real refs. If you're modeling a custom container object, pick a different name (for example, `box`) or move the mutable value into state. Renaming avoids the lint because the compiler stops inferring it as a ref.
---
## Sitemap
[Overview of all docs pages](/llms.txt)