> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/preactjs/preact/llms.txt
> Use this file to discover all available pages before exploring further.

# useLayoutEffect

> Synchronous effect that runs before browser paint

`useLayoutEffect` is identical to `useEffect`, but it fires synchronously after all DOM mutations and before the browser has a chance to paint. Use this to read layout from the DOM and synchronously re-render.

## Signature

```typescript theme={null}
function useLayoutEffect(
  effect: () => void | (() => void),
  inputs?: ReadonlyArray<unknown>
): void
```

## Parameters

<ParamField path="effect" type="() => void | (() => void)" required>
  A function containing imperative, possibly effectful code. Can optionally return a cleanup function that will be called before the effect runs again or when the component unmounts.
</ParamField>

<ParamField path="inputs" type="ReadonlyArray<unknown>" optional>
  An array of dependencies. The effect will only re-run if one of the values in this array has changed (compared using `===`). If omitted, the effect runs after every render.
</ParamField>

## Returns

`void`

## Basic Usage

```jsx theme={null}
import { useLayoutEffect, useRef, useState } from 'preact/hooks';

function MeasureElement() {
  const ref = useRef();
  const [height, setHeight] = useState(0);

  useLayoutEffect(() => {
    // Measure DOM element synchronously before paint
    if (ref.current) {
      setHeight(ref.current.offsetHeight);
    }
  }, []);

  return (
    <div>
      <div ref={ref}>Content to measure</div>
      <p>Height: {height}px</p>
    </div>
  );
}
```

## Preventing Flash of Incorrect Content

```jsx theme={null}
function Tooltip({ targetId, content }) {
  const tooltipRef = useRef();
  const [position, setPosition] = useState({ top: 0, left: 0 });

  useLayoutEffect(() => {
    const target = document.getElementById(targetId);
    const tooltip = tooltipRef.current;

    if (target && tooltip) {
      const targetRect = target.getBoundingClientRect();
      const tooltipRect = tooltip.getBoundingClientRect();

      // Calculate position before paint to avoid flicker
      setPosition({
        top: targetRect.bottom + 8,
        left: targetRect.left + (targetRect.width - tooltipRect.width) / 2
      });
    }
  }, [targetId]);

  return (
    <div
      ref={tooltipRef}
      style={{
        position: 'absolute',
        top: position.top,
        left: position.left
      }}
    >
      {content}
    </div>
  );
}
```

## Synchronizing with DOM Mutations

```jsx theme={null}
function ScrollToBottom({ messages }) {
  const containerRef = useRef();

  useLayoutEffect(() => {
    // Scroll immediately after DOM update, before paint
    if (containerRef.current) {
      containerRef.current.scrollTop = containerRef.current.scrollHeight;
    }
  }, [messages]); // Re-run when messages change

  return (
    <div ref={containerRef} style={{ height: '400px', overflow: 'auto' }}>
      {messages.map(msg => (
        <div key={msg.id}>{msg.text}</div>
      ))}
    </div>
  );
}
```

## Reading Layout Properties

```jsx theme={null}
function AdaptiveComponent() {
  const ref = useRef();
  const [isTooWide, setIsTooWide] = useState(false);

  useLayoutEffect(() => {
    if (ref.current) {
      const width = ref.current.getBoundingClientRect().width;
      setIsTooWide(width > 600);
    }
  });

  return (
    <div ref={ref} className={isTooWide ? 'compact' : 'expanded'}>
      Content that adapts to its width
    </div>
  );
}
```

## Focus Management

```jsx theme={null}
function AutoFocusInput({ shouldFocus }) {
  const inputRef = useRef();

  useLayoutEffect(() => {
    if (shouldFocus && inputRef.current) {
      // Focus before paint to avoid visible delay
      inputRef.current.focus();
    }
  }, [shouldFocus]);

  return <input ref={inputRef} type="text" />;
}
```

## Measuring and Positioning

```jsx theme={null}
function DynamicDropdown({ isOpen, children }) {
  const dropdownRef = useRef();
  const [style, setStyle] = useState({});

  useLayoutEffect(() => {
    if (isOpen && dropdownRef.current) {
      const rect = dropdownRef.current.getBoundingClientRect();
      const viewportHeight = window.innerHeight;

      // Determine if dropdown should open upward or downward
      const shouldOpenUpward = rect.bottom + 200 > viewportHeight;

      setStyle({
        position: 'absolute',
        [shouldOpenUpward ? 'bottom' : 'top']: '100%',
        left: 0,
        width: '100%'
      });
    }
  }, [isOpen]);

  return (
    <div ref={dropdownRef}>
      {isOpen && (
        <div style={style} className="dropdown-menu">
          {children}
        </div>
      )}
    </div>
  );
}
```

<Note>
  **Performance Warning**: `useLayoutEffect` runs synchronously and blocks the browser from painting. This can hurt performance if used excessively. Prefer `useEffect` when possible.
</Note>

<Note>
  Use `useLayoutEffect` when you need to:

  * Read layout from the DOM (measurements, positions)
  * Mutate the DOM synchronously before paint
  * Prevent visual flicker or "flash of incorrect content"
</Note>

<Note>
  Updates scheduled inside `useLayoutEffect` will be flushed synchronously, after all DOM mutations but before the browser paints.
</Note>
