DOM
useScript
Load an external script and report its load status.
Demo
live · imported from hookli
- status
- idle
- window.confetti
- undefined
The <script> tag mounts only when you click — nothing loads on page open.
import { useScript } from "hookli";
export function Demo() {
const status = useScript("https://example.com/widget.js");
if (status === "ready") return <p>Widget loaded.</p>;
return <p>Loading… ({status})</p>;
}Usage
import { useScript } from "hookli";
export function Demo() {
const status = useScript("https://example.com/widget.js");
if (status === "ready") return <p>Widget loaded.</p>;
return <p>Loading… ({status})</p>;
}API
useScript(src: string | null, options?: UseScriptOptions): UseScriptStatusParameters
| Name | Type | Default | Description |
|---|---|---|---|
| src | string | null | — | The script URL to load, or null to skip loading and stay idle. |
| options | UseScriptOptions | {} | Optional shouldPreventLoad and removeOnUnmount flags. |
Returns
| Name | Type | Description |
|---|---|---|
| status | UseScriptStatus | The current load status: "idle" | "loading" | "ready" | "error". |
UseScriptStatus
| Name | Type | Description |
|---|---|---|
| value | "idle" | "loading" | "ready" | "error" | Lifecycle of the injected script tag. |
UseScriptOptions
Flags controlling when the script loads and unloads.
| Name | Type | Description |
|---|---|---|
| shouldPreventLoad | boolean | Keep the status "idle" without injecting the script — useful for deferring a load. |
| removeOnUnmount | boolean | Remove the injected <script> tag when the component unmounts. |
Hook
The implementation, straight from hookli. Copy it, or install the package.
import { useEffect, useState } from "react";
type UseScriptStatus = "idle" | "loading" | "ready" | "error";
interface UseScriptOptions {
shouldPreventLoad?: boolean;
removeOnUnmount?: boolean;
}
export function useScript(
src: string | null,
options?: UseScriptOptions,
): UseScriptStatus {
const [status, setStatus] = useState<UseScriptStatus>(() => {
if (!src || options?.shouldPreventLoad) return "idle";
return "loading";
});
useEffect(() => {
if (!src || options?.shouldPreventLoad) {
setStatus("idle");
return;
}
let script = document.querySelector<HTMLScriptElement>(
`script[src="${src}"]`,
);
if (script === null) {
script = document.createElement("script");
script.src = src;
script.async = true;
script.setAttribute("data-status", "loading");
document.body.appendChild(script);
const setAttributeFromEvent = (event: Event) => {
script?.setAttribute(
"data-status",
event.type === "load" ? "ready" : "error",
);
};
script.addEventListener("load", setAttributeFromEvent);
script.addEventListener("error", setAttributeFromEvent);
} else {
setStatus(
(script.getAttribute("data-status") as UseScriptStatus) ?? "loading",
);
}
const setStateFromEvent = (event: Event) => {
setStatus(event.type === "load" ? "ready" : "error");
};
script.addEventListener("load", setStateFromEvent);
script.addEventListener("error", setStateFromEvent);
return () => {
if (script) {
script.removeEventListener("load", setStateFromEvent);
script.removeEventListener("error", setStateFromEvent);
if (options?.removeOnUnmount) {
script.remove();
}
}
};
}, [src, options?.shouldPreventLoad, options?.removeOnUnmount]);
return status;
}