Skip to content

Snippets

Short, focused code snippets for React, TypeScript, and Next.js patterns I keep coming back to.

Implementation

import { useEffect, type RefObject } from "react";

export function useOnClickOutside<T extends HTMLElement>(
  ref: RefObject<T | null>,
  handler: (event: MouseEvent | TouchEvent) => void,
) {
  useEffect(() => {
    const listener = (event: MouseEvent | TouchEvent) => {
      const el = ref.current;
      if (!el || el.contains(event.target as Node)) return;
      handler(event);
    };

    document.addEventListener("mousedown", listener);
    document.addEventListener("touchstart", listener);
    return () => {
      document.removeEventListener("mousedown", listener);
      document.removeEventListener("touchstart", listener);
    };
  }, [ref, handler]);
}

Example

function UserMenu() {
  const [open, setOpen] = useState(false);
  const menuRef = useRef<HTMLDivElement>(null);

  useOnClickOutside(menuRef, () => setOpen(false));

  return (
    <div ref={menuRef} className="relative">
      <button type="button" onClick={() => setOpen((v) => !v)} aria-expanded={open}>
        Account
      </button>
      {open && (
        <ul role="menu" className="absolute right-0 mt-2 rounded-lg border shadow-lg">
          <li role="menuitem">
            <a href="/settings">Settings</a>
          </li>
        </ul>
      )}
    </div>
  );
}
hookseventsui

Implementation

import { useState, useEffect } from "react";

export function useMediaQuery(query: string, defaultValue = false): boolean {
  const [matches, setMatches] = useState(() => {
    if (typeof window === "undefined") return defaultValue;
    return window.matchMedia(query).matches;
  });

  useEffect(() => {
    const media = window.matchMedia(query);
    const handler = (e: MediaQueryListEvent) => setMatches(e.matches);
    setMatches(media.matches);
    media.addEventListener("change", handler);
    return () => media.removeEventListener("change", handler);
  }, [query]);

  return matches;
}

Example

const isDesktop = useMediaQuery("(min-width: 1024px)");
const prefersReducedMotion = useMediaQuery(
  "(prefers-reduced-motion: reduce)",
  false,
);

// Skip heavy animation when user prefers reduced motion
const shouldAnimate = !prefersReducedMotion && isDesktop;
hooksresponsivelayout

Implementation

import { createContext, useContext, type Context } from "react";

export function createRequiredContext<T>(displayName: string) {
  const Context = createContext<T | null>(null);
  Context.displayName = displayName;

  function useRequiredContext() {
    const value = useContext(Context);
    if (value === null) {
      throw new Error(`${displayName} must be used within its Provider`);
    }
    return value;
  }

  return [Context, useRequiredContext] as const;
}

Example

import { useState } from "react";

type TabsContextValue = {
  value: string;
  setValue: (next: string) => void;
};

const [TabsContext, useTabsContext] =
  createRequiredContext<TabsContextValue>("TabsContext");

function Tabs({ children }: { children: React.ReactNode }) {
  const [value, setValue] = useState("home");
  return (
    <TabsContext.Provider value={{ value, setValue }}>
      {children}
    </TabsContext.Provider>
  );
}

function TabsTrigger({ id, children }: { id: string; children: React.ReactNode }) {
  const { value, setValue } = useTabsContext();
  return (
    <button type="button" aria-selected={value === id} onClick={() => setValue(id)}>
      {children}
    </button>
  );
}
hookscontextcompound-components

Implementation

import { useRef, useCallback, useLayoutEffect } from "react";

export function useStableCallback<T extends (...args: never[]) => unknown>(
  callback: T,
): T {
  const callbackRef = useRef(callback);

  useLayoutEffect(() => {
    callbackRef.current = callback;
  });

  return useCallback(
    ((...args) => callbackRef.current(...args)) as T,
    [],
  );
}

Example

// Accordion-style toggle: empty deps, always reads latest props
const onValueChangeRef = useRef(onValueChange);
onValueChangeRef.current = onValueChange;

const toggle = useCallback((id: string) => {
  onValueChangeRef.current?.(id);
}, []);

// Or with useStableCallback:
const toggle = useStableCallback((id: string) => {
  onValueChange?.(id);
});
hooksperformancerefs

Implementation

import { useState, useEffect } from "react";

export function useDebounce<T>(value: T, delay = 300): T {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debounced;
}

Example

const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 300);

useEffect(() => {
  if (!debouncedQuery) return;
  fetchResults(debouncedQuery);
}, [debouncedQuery]);

// input: <input value={query} onChange={(e) => setQuery(e.target.value)} />
hooksperformanceinputs