A hook that returns a signal with a counter value and functions to increment, decrement, set and reset the counter.
import { useCounter } from 'bagon-hooks';
export function UseCounterExample() {
const [count, { decrement, increment, reset, set }] = useCounter(5, { min: 1, max: 10 });
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-5 gap-x-1 rounded-md border p-3 py-10 text-center">
<span>{count()}</span>
<div class="flex gap-x-2 text-sm">
<button class="rounded-md border p-1 px-1.5 transition active:scale-90" onClick={decrement}>
-
</button>
<button class="rounded-md border p-1 px-1.5 transition active:scale-90" onClick={increment}>
+
</button>
<button class="rounded-md border p-1 px-1.5 transition active:scale-90" onClick={reset}>
Reset
</button>
<button
class="rounded-md border p-1 px-1.5 transition active:scale-90"
onClick={() => set(Math.floor(Math.random() * 10))}
>
Set (To Random)
</button>
</div>
</div>
);
}
Returns the current OS
import { useOs } from 'bagon-hooks';
export function UseOsExample() {
const os = useOs();
return (
<div class="flex h-full w-full items-center justify-center rounded-md border p-3 py-10 text-center">
Current OS: {os()}
</div>
);
}
Detects clicks outside a ref
import { useClickOutside } from 'bagon-hooks';
import { createSignal, Show } from 'solid-js';
export function UseClickOutsideExample() {
const ref = useClickOutside(() => {
setClicked(true);
setTimeout(() => {
setClicked(false);
}, 500);
});
const [clicked, setClicked] = createSignal(false);
return (
<div class="flex h-full w-full flex-1 items-center justify-center rounded-md border p-3 py-10">
<div
class={`select-none rounded-full border bg-neutral-50 p-2 text-xs transition ${clicked() ? 'scale-95' : ''}`}
ref={ref}
>
<Show when={clicked()} fallback={'No detections'}>
You clicked outside!
</Show>
</div>
</div>
);
}
Handle hotkeys easily
import { useHotkeys, useOs } from 'bagon-hooks';
import { createSignal, FlowProps, VoidProps } from 'solid-js';
export function UseHotkeysExample() {
const [activatedHotkey, setActivatedHotkey] = createSignal<-1 | 0 | 1 | 2>(-1);
let timeout: ReturnType<typeof window.setTimeout>;
function handleKeyPress(index: ReturnType<typeof activatedHotkey>) {
if (timeout) clearTimeout(timeout);
setActivatedHotkey(index);
timeout = setTimeout(() => {
setActivatedHotkey(-1);
}, 400);
}
useHotkeys([
['mod+a', () => handleKeyPress(0)],
['mod+Enter', () => handleKeyPress(1)],
['shift+g', () => handleKeyPress(2)],
]);
const os = useOs();
return (
<div class="flex h-full w-full flex-wrap items-center justify-center gap-3 rounded-md border p-3 py-10 text-center">
<Key activated={activatedHotkey() === 0}>{os() === 'macos' ? 'cmd' : 'ctrl'} + a</Key>
<Key activated={activatedHotkey() === 1}>{os() === 'macos' ? 'cmd' : 'ctrl'} + Enter</Key>
<Key activated={activatedHotkey() === 2}>shift + g</Key>
</div>
);
}
function Key(props: FlowProps<{ activated: boolean }>) {
return (
<div class="relative text-xs">
<div class="absolute inset-0 rounded-md bg-neutral-200 transition"></div>
<div
class="relative transform rounded-md border bg-neutral-50 px-2 py-1.5 transition-transform"
style={{
transform: props.activated ? 'translateY(0px)' : 'translateY(-5px)',
}}
>
{props.children}
</div>
</div>
);
}
Use this for more general keyboard events, as opposed to useHotkeys.
import { useKeyboard } from 'bagon-hooks';
import { createStore } from 'solid-js/store';
export function UseKeyboardExample() {
const [store, setStore] = createStore({ a: false, b: false, c: false, d: false });
useKeyboard({
onKeyDown(event) {
// Refactor this into if statements.
if (event.key === 'a') {
setStore('a', true);
} else if (event.key === 'b') {
setStore('b', true);
} else if (event.key === 'c') {
setStore('c', true);
} else if (event.key === 'd') {
setStore('d', true);
}
},
onKeyUp(event) {
// Refactor this into if statements.
if (event.key === 'a') {
setStore('a', false);
} else if (event.key === 'b') {
setStore('b', false);
} else if (event.key === 'c') {
setStore('c', false);
} else if (event.key === 'd') {
setStore('d', false);
}
},
});
return (
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center">
<Kbd activated={store.a}>a</Kbd>
<Kbd activated={store.b}>b</Kbd>
<Kbd activated={store.c}>c</Kbd>
<Kbd activated={store.d}>d</Kbd>
</div>
);
}
Detects hovers on a ref
import { useClickOutside, useHover } from 'bagon-hooks';
import { createSignal, Show } from 'solid-js';
export function UseHoverExample() {
const { ref, hovered } = useHover();
return (
<div class="flex h-full w-full flex-1 items-center justify-center rounded-md border p-3 py-10">
<div
class={`cursor-pointer select-none rounded-full border bg-neutral-50 p-2 text-xs transition ${hovered() ? 'scale-95' : ''}`}
ref={ref}
>
<Show when={hovered()} fallback={'No detections'}>
You hovered me!
</Show>
</div>
</div>
);
}
Returns if user has been idle after some milliseconds
import { useIdle, useOs } from 'bagon-hooks';
import { Show } from 'solid-js';
export function UseIdleExample() {
const idle = useIdle(1000);
return (
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center">
<span>Is Idle (1s):</span>
<Show
when={idle()}
fallback={
<span>
<span class="text-green-500">false</span>
</span>
}
>
<span class="text-red-500">true</span>
</Show>
</div>
);
}
Returns a random id.
import { useId } from 'bagon-hooks';
export function UseIdExample() {
const id = useId();
return (
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center text-sm">
Random ID: <span class="rounded-md bg-neutral-300 px-1.5 py-0.5">{id()}</span>
</div>
);
}
Returns true if component is mounted. Useful for rendering only on client-side.
import { useIdle, useMounted, useOs } from 'bagon-hooks';
import { Show } from 'solid-js';
export function UseMountedExample() {
const mounted = useMounted();
return (
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center">
<Show when={mounted()}>This only shows on the client.</Show>
</div>
);
}
Returns information about current user's network connection. Try going offline on DevTools.
import { useIdle, useNetwork, useOs } from 'bagon-hooks';
export function UseNetworkExample() {
const networkStatus = useNetwork();
return (
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-start">
<pre
class={`rounded-md border p-3 px-5 ${networkStatus().online ? 'bg-neutral-100' : 'border-red-500 bg-red-200'}`}
>
{JSON.stringify(networkStatus(), null, 2)}
</pre>
</div>
);
}
Returns information about a resizable element. This is more low-level but gives you more than just width and height.
{
"rect": {
"x": 0,
"y": 0,
"width": 0,
"height": 0,
"top": 0,
"left": 0,
"bottom": 0,
"right": 0
}
} import { useIdle, useNetwork, useOs, useResizeObserver } from 'bagon-hooks';
export function UseResizeObserverExample() {
const [ref, rectStore] = useResizeObserver();
return (
<div class="flex h-full w-full items-center justify-center gap-x-3 rounded-md border p-3 py-10 text-start">
<pre class={`rounded-md border bg-neutral-100 p-3 px-5 text-xs`}>
{JSON.stringify(rectStore, null, 2)}
</pre>
<div class="relative grid place-items-center overflow-hidden">
<textarea ref={ref} class="h-20 w-20 resize rounded-md border"></textarea>
<div class="pointer-events-none absolute inset-0 grid place-items-center truncate text-xs text-neutral-400">
Resize Me
</div>
</div>
</div>
);
}
Returns width and height of a resizable element. An abstraction over useResizeObserver.
import { useElementSize, useIdle, useNetwork, useOs, useResizeObserver } from 'bagon-hooks';
export function UseElementSizeExample() {
const { ref, height, width } = useElementSize();
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-3 rounded-md border p-3 py-10 text-start">
<div class="flex flex-col items-center">
<span>Width: {width().toFixed(2)}</span>
<span>Height: {height().toFixed(2)}</span>
</div>
<div class="relative grid flex-1 place-items-center overflow-hidden">
<textarea ref={ref} class="h-20 w-20 resize rounded-md border"></textarea>
<div class="pointer-events-none absolute inset-0 grid place-items-center truncate text-center text-xs text-neutral-400">
Resize Me
</div>
</div>
</div>
);
}
A hook that toggles between two or multiple values (by implementing a common state pattern). Dev Note: Personally this can be called `useCycle` instead since it cycles through the options.
import { useToggle } from 'bagon-hooks';
import { createMemo, FlowProps } from 'solid-js';
export function UseToggleExample() {
const [value, toggle] = useToggle(['apple', 'orange', 'grape', 'kiwi'] as const);
const color = createMemo(() => {
if (value() === 'apple') return '#e5312f';
if (value() === 'orange') return '#fc8627';
if (value() === 'grape') return '#bc3d73';
if (value() === 'kiwi') return '#acc144';
return undefined;
});
return (
<div
class="flex h-full w-full flex-col items-center justify-center gap-3 rounded-md border p-3 py-10 text-center transition-colors"
style={{
'background-color': color(),
}}
>
<div class="flex flex-wrap gap-3">
<Key activated={value() === 'apple'}>🍎 apple</Key>
<Key activated={value() === 'orange'}>🍊 orange</Key>
<Key activated={value() === 'grape'}>🍇 grape</Key>
<Key activated={value() === 'kiwi'}>🥝 kiwi </Key>
</div>
<button onClick={() => toggle()} class="text-white transition active:scale-95">
Click me to Toggle
</button>
</div>
);
}
function Key(props: FlowProps<{ activated: boolean }>) {
return (
<div class="relative text-xs">
<div class="absolute inset-0 rounded-md bg-neutral-200 transition"></div>
<div
class="relative transform rounded-md border bg-neutral-50 px-2 py-1.5 transition-transform"
style={{
transform: props.activated ? 'translateY(0px)' : 'translateY(-5px)',
}}
>
{props.children}
</div>
</div>
);
}
Appends <link /> element to head component with given favicon url. The hook is not called during server side rendering.
import { useFavicon } from 'bagon-hooks';
import { createSignal } from 'solid-js';
/** Improved Bagon implementation - You can choose to set it when you want. */
export function UseFaviconExample() {
const [_favicon, setFavicon] = useFavicon(); // The secret is: just don't pass an accessor in the hook.
const setXFavicon = () => {
setCurrentIcon('x');
setFavicon('https://x.com/favicon.ico');
};
const setSolidFavicon = () => {
setCurrentIcon('solid');
setFavicon('https://docs.solidjs.com/favicon.svg');
};
return (
<div class="relative flex h-full w-full flex-col items-center justify-center gap-3 overflow-hidden rounded-md border p-3 py-10 text-center text-sm">
<IconSolidJS
class="absolute -bottom-10 -right-10 h-48 w-48 rotate-45 transition"
style={{ opacity: currentIcon() === 'solid' ? 1 : 0 }}
/>
<IconX
class="absolute -bottom-10 -right-10 h-48 w-48 rotate-45 transition"
style={{ opacity: currentIcon() === 'x' ? 1 : 0 }}
/>
<button
onClick={() => {
setXFavicon();
}}
class="relative rounded-md border bg-white px-2 py-1.5 transition active:scale-95"
>
X favicon
</button>
<button
onClick={() => {
setSolidFavicon();
}}
class="relative rounded-md border bg-white px-2 py-1.5 transition active:scale-95"
>
Solid favicon
</button>
</div>
);
}
/** Based on Mantine's implementation - it always runs onMount. */
export function UseFaviconExampleMantine() {
const [favicon, setFavicon] = createSignal('https://docs.solidjs.com/favicon.svg');
const setXFavicon = () => setFavicon('https://x.com/favicon.ico');
const setSolidFavicon = () => setFavicon('https://docs.solidjs.com/favicon.svg');
useFavicon(favicon); // Will always run at the start.
return (
<>
<button onClick={setXFavicon}>Use X Favicon</button>
<button onClick={setSolidFavicon}>Use SolidJS Favicon</button>
</>
);
}
A hook that allows using value from the localStorage as a signal. The hook works exactly the same way as createSignal, but also writes the value to the localStorage. It even works between tabs! To test, try changing the value with two tabs open.
🍎 apple
🍊 orange
🍇 grape
🥝 kiwi
Favorite Fruit: apple import { useLocalStorage } from 'bagon-hooks';
import { FlowProps } from 'solid-js';
export function UseLocalStorageExample() {
const [value, setValue] = useLocalStorage({
key: 'favorite-fruit',
defaultValue: 'apple',
});
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-3 rounded-md border p-3 py-10 text-center transition-colors">
<div class="flex flex-wrap gap-3">
<Key activated={value() === 'apple'} onClick={() => setValue('apple')}>
🍎 apple
</Key>
<Key activated={value() === 'orange'} onClick={() => setValue('orange')}>
🍊 orange
</Key>
<Key activated={value() === 'grape'} onClick={() => setValue('grape')}>
🍇 grape
</Key>
<Key activated={value() === 'kiwi'} onClick={() => setValue('kiwi')}>
🥝 kiwi{' '}
</Key>
</div>
<span class="text-sm text-neutral-500">Favorite Fruit: {value()}</span>
</div>
);
}
function Key(props: FlowProps<{ activated: boolean; onClick: () => void }>) {
return (
<button onClick={props.onClick} class="relative text-xs">
<div class="absolute inset-0 rounded-md bg-neutral-200 transition"></div>
<div
class="relative transform rounded-md border bg-neutral-50 px-2 py-1.5 transition-transform"
style={{
transform: props.activated ? 'translateY(0px)' : 'translateY(-5px)',
}}
>
{props.children}
</div>
</button>
);
}
(Improvement) A hook that allows using value from the localStorage as a store for complex and efficient state management. The hook works exactly the same way as createStore, but also writes the value to the localStorage. It even works between tabs! To test, try changing the value with two tabs open.
import { useLocalStorageStore } from 'bagon-hooks';
import { For } from 'solid-js';
import { produce } from 'solid-js/store';
export function UseLocalStorageStoreExample() {
const [value, setValue] = useLocalStorageStore<{ id: string; name: string }[]>({
key: 'todos-store',
defaultValue: [],
});
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-3 rounded-md border p-3 py-10 text-center transition-colors">
<For each={value}>
{(todo, index) => (
<div class="flex flex-wrap items-center gap-1 text-sm">
<input
class="rounded-md border p-1"
value={todo.name}
onInput={event => {
setValue(
produce(_value => {
if (!_value[index()]) return;
_value[index()]!.name = event.target.value ?? '';
}),
);
}}
/>
<button
onClick={() => {
setValue(
produce(_value => {
_value.splice(index(), 1);
}),
);
}}
>
<IconClose width={18} height={18} />
</button>
</div>
)}
</For>
<button
class={`rounded-md bg-primary px-3 py-1.5 text-white transition active:scale-95`}
onClick={() => {
setValue(
produce(_value => {
_value.push({ id: Math.random().toString(), name: 'New Todo' });
}),
);
}}
>
New Todo
</button>
</div>
);
}
A hook that gives you the ability to open the browser's EyeDropper API and save it to a signal.
import { useEyeDropper } from 'bagon-hooks';
import { Show } from 'solid-js';
export function UseEyeDropperExample() {
const { color, supported, pickColor } = useEyeDropper();
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-5 rounded-md border p-3 py-10 text-center">
<div class="flex items-center gap-x-2">
<button class="transition active:scale-95" onClick={pickColor} disabled={!supported()}>
<IconEyeDropper />
</button>
<div class="flex items-center gap-x-2 text-sm">
Picked Color: {color()}
<div class="h-8 w-8 rounded-full border" style={{ 'background-color': color() }} />
</div>
</div>
<Show when={supported() !== undefined && !supported()}>
<span class="text-xs text-red-500">Your browser does not support EyeDropper.</span>
</Show>
</div>
);
}
A hook that allows you to enter and exit fullscreen mode. Can also optionally pass a ref of the element to be fullscreened.
import { useFullscreen } from 'bagon-hooks';
import { Show } from 'solid-js';
export function UseFullScreenExample() {
const { fullscreen, toggle } = useFullscreen();
return (
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center">
<button
class={`rounded-md px-3 py-1.5 text-white transition active:scale-95 ${fullscreen() ? 'bg-red-500' : 'bg-primary'}`}
onClick={toggle}
>
<Show when={fullscreen()} children={'Exit Fullscreen'} fallback={'Enter Fullscreen'} />
</button>
</div>
);
}
A hook that allows you to get and set the hash value of the current URL like a signal.
import { randomId, useHash } from 'bagon-hooks';
export function UseHashExample() {
const [hash, setHash] = useHash();
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-3 rounded-md border p-3 py-10 text-center">
<button
class="rounded-md border px-3 py-1.5 text-sm text-typography transition active:scale-95"
onClick={() => setHash(randomId())}
>
Set hash
</button>
<span class="flex gap-x-1 text-sm">
Current hash: <code class="rounded-md bg-neutral-300 px-1.5 py-0.5">{hash()}</code>
</span>
</div>
);
}
A hook that wraps copy-to-clipboard logic with a signal.
import { useClipboard } from 'bagon-hooks';
import { Show } from 'solid-js';
export function UseClipboardExample() {
const { copied, copy, reset } = useClipboard();
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-3 rounded-md border p-3 py-10 text-center text-sm">
<span class="text-center">Bagon is awesome!</span>
<button class="transition active:scale-90" onClick={() => copy('Bagon is awesome!')}>
<Show
when={copied()}
fallback={
<div class="flex items-center gap-x-1">
<IconCopy class="h-8 w-8" />
Copy
</div>
}
children={
<div class="flex items-center gap-x-1 text-green-500">
<IconCheck class="h-8 w-8" /> Copied!
</div>
}
/>
</button>
</div>
);
}
Returns the current orientation of the device. Try tilting the device (if on your phone).
{
"angle": 0,
"type": "landscape-primary"
} import { useOrientation } from 'bagon-hooks';
export function UseOrientationExample() {
const orientation = useOrientation();
return (
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center text-sm">
<pre class={`rounded-md border bg-neutral-100 p-3 px-5 text-start text-xs`}>
{JSON.stringify(orientation(), null, 2)}
</pre>
</div>
);
}
Returns a signal that tracks the current state of a media query. Try resizing the window.
import { useMediaQuery } from 'bagon-hooks';
import { Match, Switch } from 'solid-js';
export function UseMediaQueryExample() {
const sm = useMediaQuery(() => '(min-width: 640px)');
const md = useMediaQuery(() => '(min-width: 768px)');
const lg = useMediaQuery(() => '(min-width: 1024px)');
const xl = useMediaQuery(() => '(min-width: 1280px)');
const xxl = useMediaQuery(() => '(min-width: 1536px)');
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center text-sm">
<Switch fallback="No match">
<Match when={xxl()}>2xl: (min-width: 1536px)</Match>
<Match when={xl()}>xl: (min-width: 1280px)</Match>
<Match when={lg()}>lg: (min-width: 1024px)</Match>
<Match when={md()}>md: (min-width: 768px)</Match>
<Match when={sm()}>sm: (min-width: 640px)</Match>
</Switch>
</div>
);
}
Returns the current mouse position and an optional ref to the element that is being tracked.
Track Here
Mouse coordinates
{"x":0,"y":0}
import { useMouse } from 'bagon-hooks';
export function UseMouseExample() {
const { ref, position } = useMouse();
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-4 gap-x-1 rounded-md border p-3 py-10 text-center text-sm">
<div
ref={ref}
class="flex h-40 w-40 items-center justify-center rounded border bg-neutral-100 text-sm"
>
Track Here
</div>
Mouse coordinates{' '}
<code class="rounded-md bg-neutral-300 px-1.5 py-0.5">{JSON.stringify(position())}</code>
</div>
);
}
Handles move behavior over any element inside the constraints of a ref. Can be used to make custom sliders, color pickers, and draggable elements within a container.
Values: {"x":"0.50","y":"0.50"}
import { useMove } from 'bagon-hooks';
import { createSignal } from 'solid-js';
export function UseMoveExample() {
const [value, setValue] = createSignal({ x: 0.5, y: 0.5 });
const { ref, active } = useMove(({ x, y }) => {
setValue({ x, y });
}, {});
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 gap-y-3 rounded-md border p-3 py-10 text-center">
<div
ref={ref}
class="h-40 w-full rounded bg-blue-400/50"
style={{
position: 'relative',
}}
>
<div
style={{
position: 'absolute',
left: `calc(${value().x * 100}% - ${8}px)`,
top: `calc(${value().y * 100}% - ${8}px)`,
width: '16px',
height: '16px',
'background-color': active() ? '#22c55e' : '#3b82f6',
}}
/>
</div>
<div class="flex justify-center">
Values:{' '}
<code class="rounded-md bg-neutral-300 px-1.5 py-0.5">
{JSON.stringify({
x: value().x.toFixed(2),
y: value().y.toFixed(2),
})}
</code>
</div>
</div>
);
}
Creates a signal that is debounced with a given wait time.
import { useDebouncedSignal } from 'bagon-hooks';
export function UseDebouncedSignalExample() {
const [signal, setSignal] = useDebouncedSignal('', 500);
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 gap-y-2 rounded-md border p-3 py-10 text-center text-sm">
<input
value={signal()}
onInput={e => setSignal(e.currentTarget.value)}
class="rounded-md border p-2"
/>
<span>State: {JSON.stringify(signal())}</span>
</div>
);
}
Debounced value from an existing signal.
import { useDebouncedValue } from 'bagon-hooks';
import { createSignal } from 'solid-js';
export function UseDebouncedValueExample() {
const [signal, setSignal] = createSignal('');
const [value, cancel] = useDebouncedValue(signal, 500);
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 gap-y-2 rounded-md border p-3 py-10 text-center text-sm">
<input
value={signal()}
onInput={e => setSignal(e.currentTarget.value)}
class="rounded-md border p-2"
/>
<div class="flex items-center gap-x-2">
<span>State: {JSON.stringify(signal())}</span>
<span>|</span>
<span>Value: {JSON.stringify(value())}</span>
</div>
</div>
);
}
Returns the document.visibilityState - it allows detecting if the current tab is active.
import { useDocumentVisibility } from 'bagon-hooks';
export function UseDocumentVisibilityExample() {
const visible = useDocumentVisibility();
return (
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center text-sm">
<div class="flex items-center gap-x-1">
<div class="h-2 w-2 rounded-full" />
Tab is currently {visible()}
</div>
</div>
);
}
Returns intersection observer info about the element ref.
import { useIntersection } from 'bagon-hooks';
import { Show } from 'solid-js';
export function UseIntersectionExample() {
const { ref, entry } = useIntersection({
threshold: 0.75, // At least 75% of the element must "intersect" with the viewport
});
return (
<div class="relative flex h-full min-h-32 w-full items-center justify-center gap-x-1 overflow-y-scroll rounded-md border p-3 text-center text-sm">
<div class="relative top-[calc(60%)] pb-5">
<div
ref={ref}
class={`rounded-md p-5 text-white ${entry()?.isIntersecting ? 'bg-green-500' : 'bg-red-500'}`}
>
<Show
when={entry()?.isIntersecting}
children={<>Fully Intersecting</>}
fallback={<>Obscured</>}
/>
</div>
</div>
</div>
);
}
Simpler alternative to useIntersection that only returns a bool.
import { useInViewport } from 'bagon-hooks';
import { Show } from 'solid-js';
export function UseInViewportExample() {
const { ref, inViewport } = useInViewport();
return (
<div class="relative flex h-full min-h-32 w-full flex-col items-center justify-center gap-x-1 overflow-y-scroll rounded-md border p-3 text-center text-sm">
<div class="sticky left-0 right-0 top-0 text-center">
<Show
when={inViewport()}
fallback={<>Scroll to See Box</>}
children={<>Box is visible</>}
/>
</div>
<div class="relative top-[calc(60%)] pb-5 pt-20">
<div
ref={ref}
class={`rounded-md p-5 text-white ${inViewport() ? 'bg-green-500' : 'bg-red-500'}`}
>
<Show when={inViewport()} children={<>Fully Intersecting</>} fallback={<>Obscured</>} />
</div>
</div>
</div>
);
}
Handles state of native inputs with the onChange or onInput handlers. Syntax sugar over writing your own handler functions and types for the events. You can treat this as the SolidJS way of Svelte's ease of use with `bind:value` and `bind:checked`
import { useInputState } from 'bagon-hooks';
export function UseInputStateExample() {
const [input, handleInput] = useInputState('');
const [checkbox, handleCheckbox] = useInputState(false);
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 gap-y-2 rounded-md border p-3 py-10 text-center text-sm">
<pre class="rounded bg-neutral-200 p-1 text-xs">
{JSON.stringify({
input: input(),
checkbox: checkbox(),
})}
</pre>
<input value={input()} onInput={handleInput} class="rounded-md border p-2" />
<input
type="checkbox"
checked={checkbox()}
onChange={handleCheckbox}
class="rounded-md border p-2"
/>
</div>
);
}
Creates a debounced version of a callback function, delaying its execution until a specified time has elapsed since the last invocation.
import { useDebouncedCallback } from 'bagon-hooks';
import { createSignal, For, JSX, Show } from 'solid-js';
function getSearchResults(query: string): Promise<{ id: number; title: string }[]> {
return new Promise(resolve => {
setTimeout(() => {
resolve(
query.trim() === ''
? []
: Array(5)
.fill(0)
.map((_, index) => ({ id: index, title: `${query} ${index + 1}` })),
);
}, 1000);
});
}
export function UseDebouncedCallbackExample() {
const [search, setSearch] = createSignal('');
const [searchResults, setSearchResults] = createSignal<{ id: number; title: string }[]>([]);
const [loading, setLoading] = createSignal(false);
const debouncedSearch = useDebouncedCallback(async (query: string) => {
setLoading(true);
setSearchResults(await getSearchResults(query));
setLoading(false);
}, 500);
const handleInput: JSX.EventHandler<HTMLInputElement, InputEvent> = event => {
setSearch(event.currentTarget.value);
debouncedSearch(event.currentTarget.value);
};
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 gap-y-2 rounded-md border p-3 py-10 text-center text-sm">
<input
value={search()}
onInput={handleInput}
class="rounded-md border p-2"
placeholder="Search..."
/>
<Show
when={loading()}
children={<>Loading...</>}
fallback={
<For each={searchResults()}>{result => <div class="text-xs">{result.title}</div>}</For>
}
/>
</div>
);
}
This hook works the same way as createEffect but it is not called when component is mounted. Unlike createEffect, this always has a dependency of signals (like React's useEffect) for the reason that the only way to do this in Solid is using the on() + defer property under the hood.
This logs "Did Update X" to the console. Notice that it doesn't log "Did Update 0" since it happens on mount.
Simulate an Update 0 import { useDidUpdate } from 'bagon-hooks';
import { createSignal } from 'solid-js';
export function UseDidUpdateExample() {
const [signal, setSignal] = createSignal(0);
useDidUpdate(() => {
console.log('Did Update', signal());
}, signal);
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 gap-y-2 rounded-md border p-3 py-10 text-center text-sm">
<p class="max-w-xs text-center text-xs">
This logs "Did Update {signal() === 0 ? 'X' : signal()}" to the console. Notice that it
doesn't log "Did Update 0" since it happens on mount.
</p>
<button
class="rounded-md bg-primary p-2 text-white transition active:scale-95"
onClick={() => {
setSignal(signal() + 1);
}}
>
Simulate an Update {signal()}
</button>
</div>
);
}
A hook that returns system color scheme value i.e. either `dark` or `light`.
Your system color scheme is: light
import { useColorScheme } from 'bagon-hooks';
export function UseColorSchemeExample() {
const colorScheme = useColorScheme();
return (
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center text-sm">
<div
class="rounded-md border px-4 py-2 text-sm"
style={{
background: colorScheme() === 'dark' ? '#000' : '#fff',
color: colorScheme() ? '#fff' : '#000',
}}
>
Your system color scheme is: {colorScheme()}
</div>
</div>
);
}
Sets the `document.title`. Hook is not called during server-side rendering. Only use this for client-only applications.
import { useDocumentTitle, useToggle } from 'bagon-hooks';
export function UseDocumentTitleExample() {
const [title, setTitle] = useDocumentTitle();
const [_, cycle] = useToggle(['Home', 'About', 'Awesome']);
function _cycle() {
cycle();
setTitle(_() as any);
}
return (
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center text-sm">
<div class="flex flex-col items-center gap-y-7">
<div class="flex items-center gap-x-2">
<Kbd activated={title() === 'Home'}>Home</Kbd>
<Kbd activated={title() === 'About'}>About</Kbd>
<Kbd activated={title() === 'Awesome'}>Awesome</Kbd>
</div>
<button onClick={_cycle}>Toggle</button>
</div>
</div>
);
}
A simple hook to manage state for dialogs, modals, accordions, etc. Anything that needs to open/close/toggle.
import { useDisclosure } from 'bagon-hooks';
import { Show } from 'solid-js';
export function UseDisclosureExample() {
const [opened, handlers] = useDisclosure(false);
return (
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center text-sm">
<button onClick={handlers.open} class="rounded bg-blue-500 px-4 py-2 text-white">
Open Dialog
</button>
<Show when={opened()}>
<div class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div class="rounded-lg bg-white p-6 shadow-lg">
<h2 class="mb-4 text-lg font-bold">Dialog Title</h2>
<p class="mb-4">This is an example dialog using useDisclosure</p>
<button onClick={handlers.close} class="rounded bg-gray-500 px-4 py-2 text-white">
Close
</button>
</div>
</div>
</Show>
</div>
);
}