@thexjs/hooks
v0.2.1
Published
SSR-safe React hooks for the x framework (debounce, media queries, intersection observer, forms, server actions, and more).
Maintainers
Readme
@thexjs/hooks
SSR-safe React hooks for the x framework. Every hook is safe inside
renderToString / renderToReadableStream: none of them touch window,
document, localStorage, or navigator during the server render pass —
DOM access only happens inside effects, after hydration.
Install
bun add @thexjs/hooksRequires React 18 or 19.
Hooks
| Hook | Purpose |
|---|---|
| useDebounce(value, delayMs) | Debounced value for search inputs etc. |
| useLocalStorage(key, initial) | localStorage-backed state, syncs across tabs via the storage event |
| useMediaQuery(query) | Boolean from matchMedia; false on the server, hydrates on mount |
| useIntersectionObserver(ref, options) | Intersection entry for lazy-loading / infinite scroll / on-view analytics |
| useEventListener(event, handler, target?) | Typed addEventListener with automatic cleanup |
| useClickOutside(ref, handler) | Pointer-down outside a ref (dropdowns/modals) |
| usePrevious(value) | Value from the previous render |
| useCopyToClipboard() | [copiedText, copy]; async Clipboard API + execCommand fallback |
| useOnlineStatus() | navigator.onLine + online/offline listeners |
| useServerAction(fn) | { data, error, isPending } + run over a server-function client |
| useForm(initialValues, validate) | Lightweight form state + validation (works with zod, valibot, or hand-written validators) |
Example
import { useDebounce, useServerAction } from "@thexjs/hooks";
function Search() {
const [query, setQuery] = useState("");
const q = useDebounce(query, 300);
return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
}
function LikeButton() {
// `like` is a server function from the generated client
const [{ isPending, error }, run] = useServerAction(like);
return <button onClick={() => run()}>{isPending ? "..." : "Like"}</button>;
}Server actions
useServerAction expects an async function that talks to your server
function endpoint — usually one from the client generated by the @thexjs/core
build step (generateServerFunctionClient output). It adds the mutation-hook
ergonomics: { data, error, isPending } plus a run(...args) trigger.
