Skip to main content

useEffectAfterMount

Runs an effect only after the component has mounted, skipping the very first render. It behaves like useEffect, except the callback is never invoked on initial mount — only on subsequent updates to the given dependencies.

When to use it​

  • Reacting to prop/state changes without re-running logic on first render.
  • Avoiding duplicate work when an effect's initial run is already handled elsewhere (e.g. during data fetching on mount).
  • Logging or syncing side effects that should only fire on genuine updates.

Demo​

Click the button a few times and watch the console — the effect only logs on updates, never for the initial render.

Loading demo…

Signature​

function useEffectAfterMount(fn: () => void, deps?: unknown[]): void;
ParameterTypeDescription
fn() => voidThe callback to run after mount, whenever deps change.
depsunknown[]Dependency array. Defaults to [] (fn runs once per dependency change).

Returns: void

Usage notes​

  • The callback is skipped entirely on the first render — it only fires on subsequent re-renders triggered by a dependency change.
  • Useful for avoiding the common "run once on mount, then only on updates" pattern that plain useEffect doesn't support out of the box.

Hook source​

View useEffectAfterMount source

use-effect-after-mount.ts
import { useEffect, useRef } from "react";

/**
* A custom React hook that triggers a callback function only after the component has mounted
* and whenever the specified dependencies change.
*
* This hook is useful in cases where you want to skip the effect on the initial render and
* only trigger it on subsequent renders when dependencies change.
*
* @param {() => void} fn - The callback function to be executed after the component mounts and dependencies change.
* @param {unknown[]} [deps=[]] - An array of dependencies that the effect depends on. When these dependencies change,
* the callback function will be triggered. If not provided, defaults to an empty array.
*
* @example
* ```typescript
* useEffectAfterMount(() => {
* console.log("This will only log on updates, not on the initial render");
* }, [someDependency]);
* ```
*/
export const useEffectAfterMount = (fn: () => void, deps: unknown[] = []) => {
const isMounted = useRef(false);

useEffect(() => {
if (!isMounted.current) {
isMounted.current = true;
return;
}

fn();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [deps]);
};