How to debug React with console.log
Debugging React with console.log() is one of the quickest ways to understand how state, props, and events flow through your components. As the creator of CoreUI and a developer with over 25 years of experience, I reach for console.log() constantly during local development to trace exactly when and why a component updates. The key is knowing where to place your logs — inside useEffect() for state changes, inside event handlers for user interactions, and never directly in the render body for production code.
Log state changes with useEffect()
Use useEffect() to log a value every time it changes. This runs after React commits the update, so the logged value is always current.
import { useEffect, useState } from 'react'
export const SearchBox = () => {
const [query, setQuery] = useState('')
useEffect(() => {
console.log('Query changed:', query)
}, [query])
return (
<input
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder="Search..."
/>
)
}
Because useEffect() fires after the render, you see the new value of query each time it changes. This is the most reliable way to observe state transitions during development.
Strict Mode caveat: In development, React Strict Mode runs effects twice on mount. If you see duplicate logs on the initial render, that is expected behavior — it does not happen in production builds.
Log inside event handlers
To inspect values at the moment a user interacts with your component, log directly inside the handler:
const handleChange = (event) => {
console.log('Input value:', event.target.value)
setQuery(event.target.value)
}
This is useful when you need to see the raw event data before state updates. Keep in mind that logging query here would show the previous value because setQuery() is asynchronous — the state update has not been applied yet.
Log props to trace data flow
When debugging parent-child communication, log incoming props at the top of a component:
const UserCard = ({ name, role }) => {
useEffect(() => {
console.log('UserCard props:', { name, role })
}, [name, role])
return <div>{name} — {role}</div>
}
This helps you confirm whether a parent is passing the expected data and when re-renders happen.
Clean up before shipping
Remove or guard debugging logs before deploying to production. A simple environment check keeps logs available during development without cluttering production output:
if (process.env.NODE_ENV === 'development') {
console.log('Debug:', value)
}
This is the same kind of practical, low-complexity approach we use in CoreUI components to keep debugging easy without affecting production performance.
Related answers
- How to use useState in React
- How to use useEffect in React
- How to handle form inputs in React
- How to pass props in React



