npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

myjs-pad

v1.1.0

Published

A simple way to embed a JavaScript console on your website.

Readme

MyJS Pad

Embed an interactive JavaScript console on any web page with one script tag. Step-by-step code blocks, DevTools-style output, and code that runs in a Web Worker or the page itself. TypeScript works too.

Live demo · Examples · Changelog

MyJS Pad running code one block at a time, expanding an object, showing a table and an error

Usage

Add the script wherever you want the console to appear and put the code that should be loaded into the console inside of the script tag:

<div style="height: 400px;">
  <script src="https://cdn.jsdelivr.net/npm/myjs-pad@1/dist/myjs-pad.min.js">
    // Say hello \\
    console.log('Hello world!');

    // Show a table \\
    console.table([{name: 'John', age: 42}, {name: 'Jane', age: 37}]);
  </script>
</div>

The console fills its container (and is never less than 150px tall). The script is also available from unpkg at https://unpkg.com/myjs-pad@1/dist/myjs-pad.min.js.

Code Blocks

A comment line that ends with \\ is a header. Headers split the code into blocks which are run one at a time each time the run button is clicked (or Ctrl/Cmd+Enter is pressed in the editor). Ctrl/Cmd+Shift+Enter (or Run all blocks in the ⋯ menu) runs all of the remaining blocks, one after another and in order with any hidden blocks.

Hovering over code that already ran shows a "Copy to editor" button which copies that code into the editor so that it can be run again, as is or modified. If the editor already has code in it you are asked before it is replaced.

Toolbar

  • The ⋯ button opens a menu (see below).
  • Clear (or Ctrl+L) removes everything from the console. Code can also call console.clear().
  • The layout button switches between showing the editor beside or below the console.
  • Full screen shows the console using the whole screen. If the browser doesn't allow that (eg. on an iPhone) the console fills the browser window instead. Press Esc to exit.
  • Run runs the next block of code.
  • Clicking the logo opens the About window, which shows the version, this console's settings and keyboard shortcuts.
  • History (or Alt/⌥+H) lists the code that was run so it can be put back into the editor. Like the browser's console, pressing ↑ when the cursor is at the very start of the editor (with nothing selected) shows the previous code that was run and ↓ at the very end goes forward again and eventually back to the code that hasn't been run yet. After running code from the history, the code that hasn't been run yet comes back. Hidden blocks aren't in the history.
  • The ⋯ menu has:
    • Run all blocks, which runs all of the remaining blocks.
    • Text size, which is remembered for every console on the same site.
    • Open, which loads a JavaScript (or TypeScript) file as the code that the console starts with (its hidden blocks are hidden and Reset goes back to it).
    • Save, which saves the code that already ran and the code in the editor as a JavaScript (or TypeScript) file that can be opened later.
    • Pop out into a window, which moves the console into a separate window while the code keeps running in the page (so in window mode the code can still change the page). Close the window or click Bring it back to move the console back into the page.
    • Copy as HTML, which gives you the HTML for this console (with its current code, its original code or no code) as an embed snippet or a full page that you can copy or download. Only the data-* attributes that were set on the script tag are included.
    • Reset, which clears the console and puts the original code back into the editor. In worker mode this also stops any code that is still running (eg. an infinite loop) by starting a new worker.
    • About MyJS Pad

Right-Clicking Values

Right-click any value in the output (including values inside of expanded objects and arrays) for a menu with:

  • Copy as JSON and Save as JSON…. Maps and Sets become arrays and BigInts become strings. Circular references (an object inside of itself) are left out and a message says how many were left out.
  • Store as global variable, which stores the actual value (not a copy) as temp1, temp2, etc. so that later code can use it (in window mode it is stored on the page's window).
  • Copy property path (for properties and array items), which copies the path from the logged value like people[0].name.
  • Refresh (for objects, arrays, etc.), which shows the value as it is now if the code has changed it. Anything that was expanded stays expanded.

Options

Each option can be set with a data-* attribute on the script tag or, with the JavaScript API, as an option of MyJSPad.create() or MyJSPad.from(). Attribute values are always strings, while the API also takes booleans for the true/false options and an array for rulers.

| Attribute | API option | Default | Description | | --- | --- | --- | --- | | data-runner | runner | "worker" | "worker" runs the code in a Web Worker. "window" runs it directly in the page. See "Where Code Runs" below. | | data-block-type | blockType | "classic" | "classic" runs each block like a regular <script> so top-level declarations are shared between blocks. "module" runs each block like a <script type="module">. See "Module Blocks" below. | | data-language | language | "javascript" | "javascript" or "typescript". See "TypeScript" below. | | data-show-results | showResults | "true" | "false" stops the value of the last expression in each block from being shown. See "Results" below. | | data-hide-prefix | hidePrefix | None | Any block whose header starts with this prefix is hidden but still runs. See "Hidden Code" below. | | data-hide-empty-output | hideEmptyOutput | "true" | "true" only shows the editor until there is something in the output (eg. once code is run). The output then stays (even after Clear) until the console is reset. "false" always shows the output. | | data-divider-orient | dividerOrient | Automatic | "vertical" puts the editor beside the output and "horizontal" puts it below. If not given, the editor is beside the output unless the console is narrower than 600px. | | data-theme | theme | The system's | "light" or "dark". If not given, it follows the system's color scheme like the browser's dev tools. | | data-word-wrap | wordWrap | "false" | "true" wraps long lines in the editor instead of scrolling sideways. | | data-rulers | rulers | "80" | The columns where lines are shown in the editor, eg. "80, 120" (or [80, 120] with the API). "" shows none. | | data-loading | loading | "lazy" | "lazy" waits to load the console until it is about to be scrolled into view (or, if it is hidden, shown). "eager" loads it right away. See "Loading" below. | | data-imports-url | importsUrl | esm.sh | Where packages imported by name are loaded from. "" turns this off. See "Importing Packages" below. | | data-libraries-url | librariesUrl | unpkg | Where to load Vue, Ace, Prism, Acorn and Babel from. See "Self-Hosting the Libraries" below. |

Results

Like the browser's console, if the last statement in a block is an expression (eg. 2 + 2 or numbers.map(n => n * 2)) its value is shown after the block runs, unless it is undefined. Results aren't shown for hidden blocks. Use data-show-results="false" to turn this off.

Module Blocks

By default each block runs like a regular <script>, so variables declared in one block can be used in the next, but await can only be used inside of async functions. With data-block-type="module" each block runs like a <script type="module"> instead:

  • Top-level await works.
  • import works (eg. import {camelCase} from 'lodash-es';).
  • Top-level declarations stay inside of their block, just like in a module. Use globalThis to share values between blocks.

TypeScript

Add data-language="typescript" to write the code in TypeScript:

<script src="https://cdn.jsdelivr.net/npm/myjs-pad@1/dist/myjs-pad.min.js" data-language="typescript">
  // Describe a person \\
  interface Person { name: string; born: number }
  const people: Person[] = [{name: 'Ada', born: 1815}, {name: 'Grace', born: 1906}];

  // Find the oldest \\
  function oldest<T extends Person>(list: T[]): T {
    return list.reduce((a, b) => a.born <= b.born ? a : b);
  }
  oldest(people)
</script>

Before each block runs, Babel removes the types, which is how most TypeScript tools run code without a build step. The types aren't checked, so code with type errors still runs, just like it would in JavaScript. Enums, namespaces and parameter properties all work, and errors point to the lines and columns in the TypeScript code.

TypeScript works with both block types. Only type imports are removed (eg. import type {Options} from 'x' or the type Options part of import {type Options, format} from 'x'), so an import is never dropped just because nothing uses it yet. Babel is only downloaded by consoles that use TypeScript.

Importing Packages

npm packages can be imported by name. They are loaded from esm.sh, which turns npm packages into modules that work in the browser:

// In module blocks (data-block-type="module")
import _ from 'lodash';
import {format} from 'date-fns@4';

// In any block (including classic blocks), inside of async code
const {default: dayjs} = await import('dayjs');

Relative paths (eg. ./utils.js), URLs (eg. https://example.com/x.js) and node: imports are left alone. Packages that need Node.js (eg. ones that use fs) can't run in a browser.

To load packages from somewhere else set data-imports-url to a URL where {specifier} is replaced with what was imported (eg. lodash@4/fp):

| Where | data-imports-url | | --- | --- | | esm.sh (the default) | https://esm.sh/{specifier} | | jsDelivr | https://cdn.jsdelivr.net/npm/{specifier}/+esm | | Turned off (only URLs and paths can be imported) | "" |

Self-Hosting the Libraries

The console's interface uses Vue, Ace, Prism and Acorn (plus Babel for TypeScript), which are loaded from unpkg by default. Exact versions are always used so that a new release of one of them can't change how the console works.

To load them from somewhere else (eg. your own site, an intranet or another CDN) set data-libraries-url to a URL where {name} and {version} are replaced with each library's package name and version. Relative URLs are relative to the page.

| Where | data-libraries-url | | --- | --- | | unpkg (the default) | https://unpkg.com/{name}@{version}/ | | jsDelivr | https://cdn.jsdelivr.net/npm/{name}@{version}/ | | Your own copy of node_modules | /node_modules/{name}/ |

To host them yourself, install these exact versions and serve the whole package folders (Ace and Prism load other files, such as language modes, from next to their main files):

npm install [email protected] [email protected] [email protected] [email protected] [email protected]

# Only needed for TypeScript consoles
npm install @babel/[email protected]

The About window lists the versions that each version of MyJS Pad uses.

Console Functions

console.log(), info(), warn(), error(), debug(), dir(), dirxml(), table(), assert(), count(), countReset(), time(), timeLog(), timeEnd(), trace(), group(), groupCollapsed(), groupEnd() and clear() are all shown in the console.

Like the browser's console, the first argument can use format specifiers: %s (string), %d or %i (integer), %f (number), %o and %O (a value that can be expanded), %c (CSS styles for the text after it) and %% (a percent sign). For example:

console.log('%cHello%c, %s!', 'color: white; background: #1a73e8; padding: 2px 6px; border-radius: 4px', '', 'world');

Like the browser, %c only allows CSS for things like colors, fonts, borders, margins and padding, and never anything that loads a URL.

A message that is the same as the one logged right before it is shown once with a count instead of being repeated. Messages that include objects are always shown again since the objects may have changed.

Loading

Like <img loading="lazy">, a console isn't loaded until it is about to be scrolled into view, so a page with many consoles (eg. a long tutorial) only loads the ones that are read. A console that is hidden (eg. in a closed tab) loads once it is shown. Its space in the page is kept while it waits, so nothing moves around when it loads.

Until a console loads none of its code runs, including hidden blocks, and in window mode the page's console functions aren't captured yet. Add data-loading="eager" to load a console right away (eg. if its hidden blocks set up something that the page needs).

Hidden Code

If data-hide-prefix="HIDE" is specified then a block with a header like // HIDE: Setup \\ will not be shown in the editor. Instead it runs automatically (once the console loads) as soon as all of the code that came before it has run. It shows up in the output as a collapsed block labelled with whatever came after the prefix (and optional colon), or "Hidden code" if nothing did. Clicking the label shows or hides the code.

JavaScript API

Loading the script also provides MyJSPad for creating consoles from JavaScript (eg. in React, Vue or any page that adds content dynamically). A script tag in the <head> (or one anywhere with data-api-only) only provides the API, while one in the <body> is also replaced by a console (with the code inside of it) as usual.

<script src="https://cdn.jsdelivr.net/npm/myjs-pad@1/dist/myjs-pad.min.js" data-api-only></script>
const pad = MyJSPad.create({
  target: '#lesson',
  code: "// Say hello \\\\\nconsole.log('Hello!');",
  theme: 'dark',
});

// Later (eg. when a component is removed):
pad.destroy();

MyJSPad.create(options) returns {element, destroy} where element is the console's IFRAME and destroy() removes it, stops its code and closes its pop-out window (in window mode it also restores the page's console functions). Its own options are:

| Option | Description | | --- | --- | | target | Required. The element (or a CSS selector for it) that the console is placed relative to. | | placement | Where the console goes: "fill" (default) replaces the target's contents, "append" and "prepend" add it inside of the target, "replace" replaces the target itself and "before" and "after" add it next to the target. | | height | The CSS height of the console (a number is treated as pixels). Defaults to "100%" so the console fills its container. It is never less than 150px. | | code | The code that the console starts with. |

It also takes every option in Options (eg. theme or wordWrap).

MyJSPad.from(elements, options) turns existing elements (eg. the code blocks in a page made from Markdown) into consoles that start with their code:

<script src="https://cdn.jsdelivr.net/npm/myjs-pad@1/dist/myjs-pad.min.js"></script>
<script>MyJSPad.from('pre > code.language-js');</script>
  • elements is a CSS selector, an element or a list of elements (eg. an array or a NodeList).
  • Each element's text is the console's code, so code that was already highlighted (eg. by Prism or highlight.js) works too. A <code> that is the only thing in a <pre> is treated as the <pre>.
  • options are the same as for create() (except target and code) and apply to every console. placement defaults to "replace" and height defaults to a height that fits the code (from 250px to 600px).
  • The data-* attributes of each element (or its <pre>) override the options for that console (eg. data-theme="dark" or data-height="400") and a language-ts or language-typescript class turns on TypeScript.
  • It returns an array with {element, destroy} for each console, in the same order as the elements. Destroying a console that replaced its element puts the element back.

MyJSPad.version is the version of MyJS Pad that was loaded.

The package includes TypeScript types for MyJSPad (dist/myjs-pad.d.ts), which also give you autocomplete in VS Code. Install the package (eg. npm install --save-dev myjs-pad, even if the script itself comes from a CDN) and add this line to the top of a TypeScript or JavaScript file:

/// <reference types="myjs-pad" />

Where Code Runs

The console's interface is always in an IFRAME so that its styles and the page's styles can't affect each other. The code itself runs in one of two places:

  • Web Worker (data-runner="worker", the default): The code can't access the page. There is no document or DOM, but everything else (timers, fetch(), promises, etc.) works. Reset stops code that never finishes.
  • Window (data-runner="window"): The code runs directly in the page, so it can use the page's globals, functions and DOM. Only use this with code you trust. Since it replaces the page's console functions, the console also shows anything else the page logs, just like the browser's console. Reset can't undo what the code already did to the page (eg. variables it defined).

In both modes, top-level declarations (let, const, class, etc.) are shared between blocks.

Development

Install the development dependencies by running npm install.

  • npm run dev builds, rebuilds whenever one of the files in src/ changes and serves the landing page and examples at http://localhost:3000/ (opening it in your default browser) with the browser reloading automatically after each rebuild. Use BROWSER=none npm run dev to keep it from opening a browser.
  • npm run build builds the files in dist/ once.
  • npm test builds and then runs the browser tests in test/run.js using your installed copy of Google Chrome (set CHROME_PATH to use another Chromium based browser). An internet connection is needed because the console loads its libraries from CDNs. Pass part of a test's name to run only matching tests (eg. node test/run.js module).
  • As you make changes, add notes for them under Unreleased in CHANGELOG.md.
  • npm run release -- <patch|minor|major> releases a new version: it checks that you're on an up to date, clean main and that CHANGELOG.md has notes under Unreleased, runs the tests, runs npm version (which also moves those notes into a dated section for the new version), pushes main and then the tag (separately, because GitHub Pages doesn't always deploy when they are pushed together), publishes to npm and, once npm lists the new version, purges jsDelivr's cache. Add --dry-run to see what it would do or --skip-tests to skip the tests.
  • npm run record-demo records demo.gif (shown at the top of this README) from the built files. It needs ffmpeg.
  • npm run og-image makes og-image.png (the picture shown when the landing page is shared) from the built files.
  • npm run purge-cdn purges jsDelivr's cache so that URLs like myjs-pad@1 point to the latest version right away.
  • In VS Code, Terminal → Run Task… has tasks for all of these (eg. Build, Dev server, Test, Release… and Redeploy GitHub Pages).
  • npm start builds and then rebuilds whenever one of the files in src/ changes (without serving anything).

The kitchen sink example (examples/kitchen-sink.html) has a sidebar for choosing an example and every option, including which build is loaded (dist/myjs-pad.full.js, dist/myjs-pad.js or dist/myjs-pad.min.js). The options are kept in the URL so the page can be reloaded or shared as is.

Roadmap

Ideas for future versions, roughly from easiest to hardest:

  • Smaller Acorn: Acorn's package only has an unminified build (about 60 KB compressed). A minified copy is about 35 KB (jsDelivr makes one automatically, but unpkg and self-hosted copies don't), so Acorn could be loaded from jsDelivr by default or a minified copy could be published with this package.
  • data-autorun: run every visible block when the page loads.
  • Autocomplete: turn on Ace's keyword and snippet completion in the editor.
  • data-remember: save the editor's code and history in the browser so that they are still there after a reload.
  • Copy output: copy everything in the console as text from the ⋯ menu.
  • Clickable error locations: clicking snippet-2.js:3:7 in an error highlights that line in the code that ran.
  • More JavaScript API: pad.run(), pad.runAll(), pad.reset(), pad.setCode() and events (eg. pad.on('log', ...)) so that a page can control the console.
  • Output filter: search the output and show only errors, warnings or logs.
  • Exercises: hidden blocks that check the code above them and show which checks passed (eg. "sum(2, 3) should be 5").
  • @timeout: an annotation in a block's header that runs the block automatically once nothing has been logged for the given number of seconds.
  • Long output: keep the console fast when thousands of messages are logged.
  • Accessibility: announce new output to screen readers and make every value and menu reachable with the keyboard.

License

Released under the MIT License. You're free to use, modify and distribute it, including in commercial projects, as long as the copyright and license notice is kept.

Copyright (c) 2023-present Chris West