To initialize state lazily with useState, pass a function instead of calling it yourself. React runs that initializer only during the first render, so expensive setup is not repeated on every update. The result of the function becomes the initial value.
Why lazy initialization helps
useState accepts an initial value. The problem appears when computing that value is slow, like building a large list or reading from storage.
The code that produces the value would otherwise run on every render, even though React keeps only the first result. Every keystroke or click re-runs the component, so that setup cost would repeat with each update.
Lazy initialization fixes this by deferring the work. React calls your function once, stores what it returns, and ignores the argument on later renders. The result is identical on screen; only the cost changes.
Pass an initializer function
The difference is one pair of parentheses. Pass the function itself, not the result of calling it.
import { useState } from "react";
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
}Here React calls createInitialTodos once during the first render and stores its array as the initial state. On later renders, the function is not called again.
The incorrect version calls the function every render:
const [todos, setTodos] = useState(createInitialTodos());The result is the same on screen, but the setup work runs again and again for no reason. For a function that loops fifty times, the difference is small. For one that reads a large file or computes a heavy object, it adds up quickly.
Think of it as deferring the calculation. React stores a note to run the function when it first needs the value, instead of running it right now.
What the initializer looks like
The initializer is a plain function with no arguments. It builds and returns the starting value.
function createInitialTodos() {
const list = [];
for (let i = 0; i < 50; i++) {
list.push({ id: i, title: "Item " + (i + 1) });
}
return list;
}React stores whatever this function returns as the first value of the state. The function must be pure: it should not change anything outside itself, because React may call it more than once in development.
Initial value from props
An initializer receives no arguments, so it cannot react to props changing. If the starting value depends on a prop, the state only captures that prop's first value.
const [draft, setDraft] = useState(() => createDraft(initialText));When initialText changes later, draft does not reset, because React already stored the first result. If a value should always follow a prop, compute it during render instead of copying it into state.
When lazy initialization is not needed
Most state starts from a simple value, like a number, a string, or a short object. For those, pass the value directly.
const [count, setCount] = useState(0);Reserve the function form for setup that actually costs something. A boolean, an empty string, or a small constant is clear enough as a direct value, and the function form only adds noise. The useState guide covers the basic value form, and the same pass-a-function idea appears in updater functions, where React calls your function with the latest value.
Common mistakes
- Calling the initializer yourself, which defeats the purpose and runs the work every render.
- Putting side effects in the initializer, which Strict Mode can run twice in development.
- Using lazy initialization for trivial values where a plain value is clearer.
If the initial value is itself a function, wrap it in an arrow so React stores the function instead of calling it. The initializer runs only once, but it must still be deterministic so development checks do not change the result. A pure initializer passes those checks every time.
What to learn next
Lazy initialization pairs naturally with lists built in state, which you can then update with the array operations. Next, learn how React groups your updates into one render, and why that timing matters.
Rune AI
Key Insights
- Pass an initializer function to useState, not its result.
- React runs the initializer only during the first render.
- Calling it yourself runs the work on every render.
- Keep the initializer pure with no side effects.
- Use lazy initialization only when the setup is expensive.
Frequently Asked Questions
What does lazy initialization mean in React?
Why not just call the function myself?
Does React call my initializer twice?
Conclusion
Pass the function itself to useState to initialize lazily. React calls it once during the first render, so expensive setup is not repeated on every update.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.