A lightweight, extendable collection of missing React-like hooks for Preact — plus fresh, powerful new ones designed specifically for modern Preact apps.
npm install preact-missing-hooksuseTransition — Defers state updates to yield a smoother UI experience.
useMutationObserver — Reactively observes DOM changes with a familiar hook API.
useEventBus — A simple publish/subscribe system, eliminating props drilling or overuse of context.
useWrappedChildren — Injects props into child components with flexible merging strategies.
usePreferredTheme — Detects the user's preferred color scheme (light/dark) from system preferences.
useNetworkState — Tracks online/offline status and connection details (type, downlink, RTT, save-data).
useClipboard — Copy and paste text with the Clipboard API, with copied/error state.
preact)
bash
npm install preact-missing-hooks
`
---
Usage Examples
$3
`tsx
import { useTransition } from 'preact-missing-hooks'
function ExampleTransition() {
const [startTransition, isPending] = useTransition()
const handleClick = () => {
startTransition(() => {
// perform an expensive update here
})
}
return (
)
}
`
---
$3
`tsx
import { useRef } from 'preact/hooks'
import { useMutationObserver } from 'preact-missing-hooks'
function ExampleMutation() {
const ref = useRef(null)
useMutationObserver(
ref,
(mutations) => {
console.log('Detected mutations:', mutations)
},
{ childList: true, subtree: true },
)
return Observe this content
}
`
---
$3
`tsx
// types.ts
export type Events = {
notify: (message: string) => void
}
// Sender.tsx
import { useEventBus } from 'preact-missing-hooks'
import type { Events } from './types'
function Sender() {
const { emit } = useEventBus()
return
}
// Receiver.tsx
import { useEventBus } from 'preact-missing-hooks'
import { useState, useEffect } from 'preact/hooks'
import type { Events } from './types'
function Receiver() {
const [msg, setMsg] = useState('')
const { on } = useEventBus()
useEffect(() => {
const unsubscribe = on('notify', setMsg)
return unsubscribe
}, [])
return Message: {msg}
}
`
---
$3
`tsx
import { useWrappedChildren } from 'preact-missing-hooks'
function ParentComponent({ children }) {
// Inject common props into all children
const injectProps = {
className: 'enhanced-child',
onClick: () => console.log('Child clicked!'),
style: { border: '1px solid #ccc' },
}
const wrappedChildren = useWrappedChildren(children, injectProps)
return {wrappedChildren}
}
// Usage with preserve strategy (default - existing props are preserved)
function PreserveExample() {
return (
Both styles applied
)
}
// Usage with override strategy (injected props override existing ones)
function OverrideExample() {
const injectProps = { className: 'new-class' }
const children = (
)
const wrappedChildren = useWrappedChildren(children, injectProps, 'override')
return {wrappedChildren}
}
`
---
$3
`tsx
import { usePreferredTheme } from 'preact-missing-hooks'
function ThemeAwareComponent() {
const theme = usePreferredTheme() // 'light' | 'dark' | 'no-preference'
return Your system prefers: {theme}
}
`
---
$3
`tsx
import { useNetworkState } from 'preact-missing-hooks'
function NetworkStatus() {
const { online, effectiveType, saveData } = useNetworkState()
return (
Status: {online ? 'Online' : 'Offline'}
{effectiveType && (${effectiveType})}
{saveData && ' — Reduced data mode enabled'}
)
}
`
---
$3
`tsx
import { useState } from 'preact/hooks'
import { useClipboard } from 'preact-missing-hooks'
function CopyButton() {
const { copy, copied, error } = useClipboard({ resetDelay: 2000 })
return (
)
}
function PasteInput() {
const [text, setText] = useState('')
const { paste } = useClipboard()
const handlePaste = async () => {
const content = await paste()
setText(content)
}
return (
setText(e.target.value)} />
)
}
``