> ## 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.

# Refs

> Access DOM nodes and persist values across renders

Refs provide a way to access DOM nodes or Preact elements created in the render method. They're also useful for storing mutable values that persist across renders without causing re-renders.

## When to Use Refs

Refs are useful for:

* Managing focus, text selection, or media playback
* Triggering imperative animations
* Integrating with third-party DOM libraries
* Storing values that don't affect rendering

<Warning>
  Don't overuse refs. Most things should be done declaratively with props and state.
</Warning>

## Creating Refs

Preact provides two ways to create refs:

<Tabs>
  <Tab title="useRef Hook (Recommended)">
    The `useRef` hook is the recommended way to create refs in function components:

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

    function TextInput() {
      const inputRef = useRef(null);

      const focusInput = () => {
        inputRef.current.focus();
      };

      return (
        <div>
          <input ref={inputRef} type="text" />
          <button onClick={focusInput}>Focus Input</button>
        </div>
      );
    }
    ```

    ### How useRef Works

    The `useRef` hook is implemented in `hooks/src/index.js` as a simple wrapper around `useMemo`:

    ```javascript theme={null}
    // From hooks/src/index.js:306-309
    export function useRef(initialValue) {
      currentHook = 5;
      return useMemo(() => ({ current: initialValue }), []);
    }
    ```

    It returns an object with a `current` property that persists across renders.

    Reference: `hooks/src/index.js:306-309`
  </Tab>

  <Tab title="createRef">
    For class components or when you need to create a ref outside of a component, use `createRef`:

    ```jsx theme={null}
    import { Component, createRef } from 'preact';

    class TextInput extends Component {
      inputRef = createRef();

      focusInput = () => {
        this.inputRef.current.focus();
      };

      render() {
        return (
          <div>
            <input ref={this.inputRef} type="text" />
            <button onClick={this.focusInput}>Focus Input</button>
          </div>
        );
      }
    }
    ```

    ### createRef Implementation

    The `createRef` function is simple - it just returns an object:

    ```javascript theme={null}
    // From src/create-element.js:73-75
    export function createRef() {
      return { current: null };
    }
    ```

    Reference: `src/create-element.js:73-75`
  </Tab>
</Tabs>

## Accessing DOM Elements

The most common use of refs is to access DOM elements directly:

<CodeGroup>
  ```jsx Focus Management theme={null}
  import { useRef, useEffect } from 'preact/hooks';

  function SearchInput() {
    const inputRef = useRef(null);

    useEffect(() => {
      // Focus input on mount
      inputRef.current.focus();
    }, []);

    return <input ref={inputRef} placeholder="Search..." />;
  }
  ```

  ```jsx Scroll Control theme={null}
  import { useRef } from 'preact/hooks';

  function ScrollToBottom() {
    const bottomRef = useRef(null);

    const scrollToBottom = () => {
      bottomRef.current.scrollIntoView({ behavior: 'smooth' });
    };

    return (
      <div>
        <div style={{ height: '2000px' }}>Long content...</div>
        <div ref={bottomRef}>Bottom</div>
        <button 
          onClick={scrollToBottom}
          style={{ position: 'fixed', bottom: 20, right: 20 }}
        >
          Scroll to Bottom
        </button>
      </div>
    );
  }
  ```

  ```jsx Video Control theme={null}
  import { useRef } from 'preact/hooks';

  function VideoPlayer({ src }) {
    const videoRef = useRef(null);

    const play = () => videoRef.current.play();
    const pause = () => videoRef.current.pause();
    const restart = () => {
      videoRef.current.currentTime = 0;
      videoRef.current.play();
    };

    return (
      <div>
        <video ref={videoRef} src={src} />
        <button onClick={play}>Play</button>
        <button onClick={pause}>Pause</button>
        <button onClick={restart}>Restart</button>
      </div>
    );
  }
  ```
</CodeGroup>

## Callback Refs

Instead of passing a ref object, you can pass a function. Preact will call it with the DOM element:

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

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

  const measuredRef = element => {
    if (element) {
      setHeight(element.getBoundingClientRect().height);
    }
  };

  return (
    <div>
      <div ref={measuredRef}>
        <p>This element's height is: {height}px</p>
      </div>
    </div>
  );
}
```

<Note>
  Callback refs are called with `null` when the component unmounts. Always check if the element exists.
</Note>

## Storing Mutable Values

Refs aren't just for DOM elements. They're perfect for storing values that need to persist across renders but shouldn't trigger re-renders when changed:

<CodeGroup>
  ```jsx Timers theme={null}
  import { useRef, useState, useEffect } from 'preact/hooks';

  function Stopwatch() {
    const [time, setTime] = useState(0);
    const intervalRef = useRef(null);

    const start = () => {
      if (intervalRef.current) return; // Already running
      
      intervalRef.current = setInterval(() => {
        setTime(t => t + 1);
      }, 1000);
    };

    const stop = () => {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
    };

    const reset = () => {
      stop();
      setTime(0);
    };

    useEffect(() => {
      return () => stop(); // Cleanup on unmount
    }, []);

    return (
      <div>
        <div>Time: {time}s</div>
        <button onClick={start}>Start</button>
        <button onClick={stop}>Stop</button>
        <button onClick={reset}>Reset</button>
      </div>
    );
  }
  ```

  ```jsx Previous Values theme={null}
  import { useRef, useEffect } from 'preact/hooks';

  function usePrevious(value) {
    const ref = useRef();
    
    useEffect(() => {
      ref.current = value;
    });
    
    return ref.current;
  }

  // Usage
  function Counter({ count }) {
    const prevCount = usePrevious(count);
    
    return (
      <div>
        <p>Current: {count}</p>
        <p>Previous: {prevCount}</p>
        <p>Changed by: {count - prevCount}</p>
      </div>
    );
  }
  ```

  ```jsx Instance Variables theme={null}
  import { useRef } from 'preact/hooks';

  function EventLog() {
    const eventCountRef = useRef(0);
    const eventsRef = useRef([]);

    const logEvent = (event) => {
      eventCountRef.current++;
      eventsRef.current.push({
        id: eventCountRef.current,
        type: event.type,
        time: Date.now()
      });
      
      console.log(`Total events: ${eventCountRef.current}`);
    };

    return (
      <button onClick={logEvent}>
        Click me (Events: {eventCountRef.current})
      </button>
    );
  }
  ```
</CodeGroup>

## Forwarding Refs

Sometimes you need to pass a ref through a component to one of its children. Use `forwardRef` from `preact/compat`:

<Steps>
  ### Basic Ref Forwarding

  ```jsx theme={null}
  import { forwardRef } from 'preact/compat';

  const FancyInput = forwardRef((props, ref) => (
    <div className="fancy-input">
      <input ref={ref} {...props} />
    </div>
  ));

  // Usage
  function Parent() {
    const inputRef = useRef(null);

    return (
      <div>
        <FancyInput ref={inputRef} />
        <button onClick={() => inputRef.current.focus()}>
          Focus Input
        </button>
      </div>
    );
  }
  ```

  ### With useImperativeHandle

  Customize what the ref exposes using `useImperativeHandle`:

  ```jsx theme={null}
  import { forwardRef } from 'preact/compat';
  import { useRef, useImperativeHandle } from 'preact/hooks';

  const VideoPlayer = forwardRef((props, ref) => {
    const videoRef = useRef(null);

    useImperativeHandle(ref, () => ({
      play: () => videoRef.current.play(),
      pause: () => videoRef.current.pause(),
      restart: () => {
        videoRef.current.currentTime = 0;
        videoRef.current.play();
      }
    }));

    return <video ref={videoRef} src={props.src} />;
  });

  // Usage
  function Parent() {
    const playerRef = useRef(null);

    return (
      <div>
        <VideoPlayer ref={playerRef} src="video.mp4" />
        <button onClick={() => playerRef.current.play()}>Play</button>
        <button onClick={() => playerRef.current.pause()}>Pause</button>
        <button onClick={() => playerRef.current.restart()}>Restart</button>
      </div>
    );
  }
  ```

  ### Implementation Details

  The `forwardRef` function is implemented in `compat/src/forwardRef.js`:

  ```javascript theme={null}
  // From compat/src/forwardRef.js:12-31
  export function forwardRef(fn) {
    function Forwarded(props) {
      let clone = assign({}, props);
      delete clone.ref;
      return fn(clone, props.ref || null);
    }

    Forwarded.$$typeof = Symbol.for('react.forward_ref');
    Forwarded.render = fn;
    Forwarded.prototype.isReactComponent = true;
    Forwarded.displayName = 'ForwardRef(' + (fn.displayName || fn.name) + ')';

    return Forwarded;
  }
  ```

  Reference: `compat/src/forwardRef.js:12-31`
</Steps>

## Ref Best Practices

<Steps>
  ### Check for null

  Always check if the ref exists before accessing it:

  ```jsx theme={null}
  function Component() {
    const ref = useRef(null);

    const handleClick = () => {
      // Good: Check before accessing
      if (ref.current) {
        ref.current.focus();
      }
    };

    return <input ref={ref} />;
  }
  ```

  ### Don't access refs during render

  Refs should be accessed in event handlers or effects, not during render:

  ```jsx theme={null}
  function Component() {
    const ref = useRef(null);

    // Bad: Accessing during render
    const width = ref.current?.offsetWidth;

    // Good: Access in effect
    useEffect(() => {
      const width = ref.current?.offsetWidth;
      console.log(width);
    }, []);

    return <div ref={ref}>Content</div>;
  }
  ```

  ### Use refs sparingly

  Most things should be done declaratively. Only use refs when necessary:

  ```jsx theme={null}
  // Bad: Using ref for something that should be state
  function Component() {
    const countRef = useRef(0);
    
    return (
      <button onClick={() => countRef.current++}>
        Count: {countRef.current} {/* Won't update! */}
      </button>
    );
  }

  // Good: Use state for values that affect rendering
  function Component() {
    const [count, setCount] = useState(0);
    
    return (
      <button onClick={() => setCount(c => c + 1)}>
        Count: {count}
      </button>
    );
  }
  ```

  ### Clean up in effects

  When using refs with effects, clean up properly:

  ```jsx theme={null}
  function Component() {
    const ref = useRef(null);

    useEffect(() => {
      const element = ref.current;
      if (!element) return;

      const observer = new ResizeObserver(() => {
        console.log('Resized');
      });

      observer.observe(element);

      return () => {
        observer.unobserve(element);
      };
    }, []);

    return <div ref={ref}>Content</div>;
  }
  ```
</Steps>

## Common Patterns

<CodeGroup>
  ```jsx Auto-focus theme={null}
  import { useRef, useEffect } from 'preact/hooks';

  function AutoFocusInput({ autoFocus, ...props }) {
    const ref = useRef(null);

    useEffect(() => {
      if (autoFocus && ref.current) {
        ref.current.focus();
      }
    }, [autoFocus]);

    return <input ref={ref} {...props} />;
  }
  ```

  ```jsx Click Outside theme={null}
  import { useRef, useEffect } from 'preact/hooks';

  function useClickOutside(callback) {
    const ref = useRef(null);

    useEffect(() => {
      const handleClick = (event) => {
        if (ref.current && !ref.current.contains(event.target)) {
          callback();
        }
      };

      document.addEventListener('mousedown', handleClick);
      return () => document.removeEventListener('mousedown', handleClick);
    }, [callback]);

    return ref;
  }

  // Usage
  function Dropdown() {
    const [isOpen, setIsOpen] = useState(false);
    const dropdownRef = useClickOutside(() => setIsOpen(false));

    return (
      <div ref={dropdownRef}>
        <button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
        {isOpen && <div className="menu">Menu content</div>}
      </div>
    );
  }
  ```

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

  function useDimensions() {
    const ref = useRef(null);
    const [dimensions, setDimensions] = useState({ width: 0, height: 0 });

    useEffect(() => {
      if (!ref.current) return;

      const observer = new ResizeObserver(entries => {
        const { width, height } = entries[0].contentRect;
        setDimensions({ width, height });
      });

      observer.observe(ref.current);
      return () => observer.disconnect();
    }, []);

    return [ref, dimensions];
  }

  // Usage
  function Component() {
    const [ref, { width, height }] = useDimensions();

    return (
      <div ref={ref}>
        Width: {width}px, Height: {height}px
      </div>
    );
  }
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Hooks" icon="hook" href="/guides/working-with-hooks">
    Learn about useRef and other hooks in depth
  </Card>

  <Card title="Fragments" icon="puzzle-piece" href="/guides/fragments">
    Return multiple elements without wrapper nodes
  </Card>
</CardGroup>
