Third-party widgets — Stripe, a maps SDK, an analytics snippet — ship as a <script src> you're supposed to drop in the page <head>. In a SPA you often want to load them lazily, only on the route that needs them, and know when they're ready so you can safely touch their globals. useScript does that: give it a URL, it injects the tag once and returns a status you can render against.
Implement useScript(src). It returns 'idle' | 'loading' | 'ready' | 'error'. It appends the <script> to the body, tracks its load/error events, and — crucially — dedupes: if the same src is already on the page, it reuses that tag instead of injecting a duplicate.
function useScript(src) {
// returns 'idle' | 'loading' | 'ready' | 'error'
}
function Checkout() {
const status = useScript('https://js.stripe.com/v3');
if (status === 'ready') return <StripeForm />;
if (status === 'error') return <p>Failed to load payments.</p>;
return <Spinner />;
}
// Two components both call useScript(sameSrc) — only ONE <script> is injected.
const a = useScript('/maps.js'); // injects
const b = useScript('/maps.js'); // reuses, reports the same status
<script>, set src and async, append to document.body.ready/error; start at loading (or idle when src is falsy).script[src="…"]; if found, don't inject again — read its current status from a data-status attribute you keep updated.src changes (leave the tag so other consumers keep working).