Directives and transitions
Sometimes a component needs the element itself: to focus it, measure it, hand it to a charting library, or listen for something JSX has no attribute for. A directive is a function that receives the element when it's created.
A directive
export function autofocus(node: Element) {
(node as HTMLElement).focus();
}import { autofocus } from "../lib/autofocus";
const Search = () => <input type="search" name="q" use={autofocus} />;
export default Search;use={fn} calls fn(element) once, when the element is created. On a server-rendered page that's when the page hydrates; on the server itself directives don't run.
Parameters and cleanup
Pass a tuple to give the directive a second argument, and return a function to run cleanup when the element goes away:
export function clickOutside(node: Element, onOutside: () => void) {
const listener = (e: MouseEvent) => {
if (!node.contains(e.target as Node)) onOutside();
};
document.addEventListener("click", listener, true);
return () => document.removeEventListener("click", listener, true);
}import { clickOutside } from "./clickOutside";
export const Menu = () => {
let open = $state(false);
return (
<div class="menu" use={[clickOutside, () => (open = false)]}>
<button onClick={() => (open = !open)}>Menu</button>
{open && (
<ul>
<li>
<a href="/settings">Settings</a>
</li>
</ul>
)}
</div>
);
};The cleanup runs when the component that rendered the element is torn down, for example when the router navigates to another page.
enhance from @sigil-dev/grimoire/client is a directive like these (Form actions).
Transitions
transition:, in: and out: animate an element as it appears and disappears. The part after the colon is a label for readers; the value is a function that receives the element and returns a Web Animation:
export function fade(el: Element, ms: unknown, direction: "in" | "out") {
return el.animate([{ opacity: 0 }, { opacity: 1 }], {
duration: typeof ms === "number" ? ms : 200,
direction: direction === "in" ? "normal" : "reverse",
easing: "ease-out",
});
}import { fade } from "./fade";
export const Notice = () => {
let shown = $state(true);
return (
<>
<button onClick={() => (shown = !shown)}>Toggle</button>
{shown && <p transition:fade={[fade, 300]}>Saved.</p>}
</>
);
};in:plays when the element is created.out:plays when the element is removed, and the element stays in the page until the animation finishes.transition:does both.
Pass [fn, params] to hand the function a second argument, as with use.
@sigil-dev/runtime exports easing functions (easeOutCubic, easeInOutQuad, and so on) for animations you drive yourself (Runtime).