Skip to main content

useOnMountLayout

Executes a side effect synchronously only once when the component mounts, before the browser paints. This hook is the synchronous version of useOnMount, using useLayoutEffect to ensure the effect runs before the browser updates the screen. This makes it ideal for DOM manipulations that must complete before the user sees the initial render.

When to Use Over useOnMount

  • Preventing Flash of Unstyled Content (FOUC): Apply styles before first paint
  • Initial DOM Measurements: Get accurate dimensions for layout calculations
  • Scroll Position Restoration: Set scroll position without visible jumps
  • Focus Management: Set initial focus without delay
  • Third-party UI Libraries: Initialize libraries that manipulate DOM immediately

Performance Warning

Since this blocks painting, use sparingly. Only use when synchronous behavior is necessary to prevent visual issues.

Signature

const useOnMountLayout: (handler: EffectCallback) => void

Parameters

NameTypeDescription
handler-The effect function to execute synchronously on mount. Can return a cleanup function

Examples

Example 1

// Prevent layout shift by measuring before paint
useOnMountLayout(() => {
const element = containerRef.current;
if (!element) return;

const height = element.scrollHeight;
element.style.height = '0px';

// Trigger reflow for animation
element.offsetHeight;
element.style.height = `${height}px`;
});

// Apply theme before first paint to prevent flicker
useOnMountLayout(() => {
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.documentElement.classList.add('dark-mode');
}
});

// Initialize tooltip library that needs immediate DOM access
useOnMountLayout(() => {
const tooltips = tippy('[data-tooltip]', {
placement: 'top',
animation: 'fade',
});

return () => {
tooltips.forEach(instance => instance.destroy());
};
});

// Restore scroll position without jump
useOnMountLayout(() => {
const scrollY = sessionStorage.getItem('scrollPosition');
if (scrollY) {
window.scrollTo(0, parseInt(scrollY, 10));
sessionStorage.removeItem('scrollPosition');
}
});

// Set initial focus for accessibility
useOnMountLayout(() => {
const firstInput = document.querySelector<HTMLInputElement>(
'input:not([disabled]), textarea:not([disabled])'
);
firstInput?.focus();
});

Playground

// Prevent layout shift by measuring before paint
useOnMountLayout(() => {
const element = containerRef.current;
if (!element) return;

const height = element.scrollHeight;
element.style.height = '0px';

// Trigger reflow for animation
element.offsetHeight;
element.style.height = `${height}px`;
});

// Apply theme before first paint to prevent flicker
useOnMountLayout(() => {
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
  document.documentElement.classList.add('dark-mode');
}
});

// Initialize tooltip library that needs immediate DOM access
useOnMountLayout(() => {
const tooltips = tippy('[data-tooltip]', {
  placement: 'top',
  animation: 'fade',
});

return () => {
  tooltips.forEach(instance => instance.destroy());
};
});

// Restore scroll position without jump
useOnMountLayout(() => {
const scrollY = sessionStorage.getItem('scrollPosition');
if (scrollY) {
  window.scrollTo(0, parseInt(scrollY, 10));
  sessionStorage.removeItem('scrollPosition');
}
});

// Set initial focus for accessibility
useOnMountLayout(() => {
const firstInput = document.querySelector<HTMLInputElement>(
  'input:not([disabled]), textarea:not([disabled])'
);
firstInput?.focus();
});