
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
add-javascript
Advanced tools
Just add JavaScript. Includes validation, integrity checking, caching, and a cross-framework hook.
add-javascript 📜Just add JavaScript.
Includes validation, integrity checking, caching, and a cross-framework hook.
addScript()import { addScript } from "add-javascript";
addScript("https://www.example.com/script.js", options);
// "added" | "already-added" | "already-added (unmanaged)" | "skipped"
//
// "unmanaged" - means the script was not added by `add-javascript`
//
// "skipped" - means `skipLoading()` returned true. Callbacks will
// still have run, so this does not imply a no-op.
//
options (defaults shown, see index.d.ts for all, loadBehavior visualised here):
// options:
{
isModule: false,
loadBehavior: "async",
fetchPriority: "auto",
noopIfModulesSupported: false,
ignoreQueryString: true,
security: {
crossOrigin: "",
nonce: "",
integrity: "",
referrerPolicy: ""
},
dataSet: {},
skipLoading: () => false,
onLoad: detail => {},
onError: detail => {}
}
// detail:
{
event: Event,
removeScriptTag: () => void
}
loadScript()import { loadScript } from "add-javascript";
loadScript("https://www.example.com/script.js", {
// Same options as addScript(), but
// without onLoad and onError since we
// can use Promise callbacks instead
})
.then(successDetail => {})
.catch(failDetail => {});
// successDetail:
{
type: "added" | "already-added" | "already-added (unmanaged)" | "skipped",
event: Event,
removeScriptTag: () => void
}
// failDetail:
{
type: "error",
event: Event,
removeScriptTag: () => void
}
// Multiple scripts
await Promise.all([
loadScript("https://www.example.com/script-1.js", options),
loadScript("https://www.example.com/script-2.js", options)
]);
// ...or if you have common options:
await Promise.all(
[
"https://www.example.com/script-1.js",
"https://www.example.com/script-2.js"
].map(src => loadScript(src, options))
);
makeHook()React / Preactimport { makeHook } from "add-javascript";
import { useState, useEffect } from "react";
// Make the Hook
const useScriptReact = makeHook({ useState, useEffect });
function ReactComponent(props) {
// Use it
const [state, detail] = useScriptReact("./my-script.js", options);
// state == "pending" | "loading" | "loaded" | "error"
// detail == successDetail | failDetail
return <div>{state}</div>;
}
Mithrilimport { makeHook } from "add-javascript";
import { withHooks, useState, useEffect } from "mithril-hooks";
// Make the Hook
const useScriptMithril = makeHook({ useState, useEffect });
const MithrilComponent = withHooks(() => {
// Use it
const [state, detail] = useScriptMithril("./my-script.js", options);
// state == "pending" | "loading" | "loaded" | "error"
// detail == successDetail | failDetail
return m("div", state);
});
SolidJSimport { makeHook } from "add-javascript";
import { createSignal, createEffect } from "solid-js";
// Make the Hook
const useScriptSolidJS = makeHook({
useState: createSignal,
useEffect: createEffect
});
function SolidJSComponent() {
// Use it
const [state, detail] = useScriptSolidJS("./my-script.js", options);
// state() == "pending" | "loading" | "loaded" | "error"
// detail() == successDetail | failDetail
return html`${state()}`;
}
You can use all of these frameworks on the same page (if you like.) Check out the tests for a working implementation of this.
Create useScript in its own file for your convenience:
// useScript.js
import { makeHook } from "add-javascript";
import { useState, useEffect } from "react";
export const useScript = makeHook({ useState, useEffect });
// ReactComponent.js
import { useScript } from "./useScript";
function ReactComponent(props) {
const [state] = useScript("./my-script.js");
return <div>{state}</div>;
}
$ pnpm i add-javascript
Supports import/require for ESM/CJS.
Browser/UMD version here:
<script src="https://unpkg.com/add-javascript/dist/browser/add-javascript.browser.js"></script>
<script>
const { loadScript } = addJs;
</script>
"There are loooads of loadScript/useScript libraries. Why make another one?"
Good question. Perhaps I didn't really need my own library, but I once found myself battling a bug caused by Hot Module Reloading vs. a 3rd-party script and I just ended up writing one to solve the problem. This is the result.
As for the bug, the size of the codebase I was working on at the time make the problem difficult to debug. Long story short, the 3rd-party script was being loaded multiple times and was also not idempotent.
There was also a chance it could be loaded in a vanilla way or via a Hook, hence the desire to offer an API that would consolidate the script-adding logic. This also explains the options default of ignoreQueryString: true, which considers a script loaded regardless of its (in my case: cache-busting) query-string parameters.
The options format is also something of an itch I wanted to scratch.
All that is behind me now, but perhaps someone else will find the outcome of these efforts useful.
Still, if your codebase is under your full control you're likely far better just rolling your own little helper than installing add-javascript:
const loadScript = (src, options = { async: true }) =>
new Promise((resolve, reject) => {
const script = document.createElement("script");
Object.assign(script, options);
script.onload = resolve;
script.onerror = reject;
script.src = src;
document.body.appendChild(script);
});
add-javascript was written by Conan Theobald.
I hope you found it useful! If so, I like coffee ☕️ :)
MIT licensed: See LICENSE
FAQs
Just add JavaScript. Includes validation, integrity checking, caching, and a cross-framework hook.
We found that add-javascript demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.