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

@aweftjs/icons

v0.1.1

Published

Icon sets as modules: one icon at build time, a whole set, the standard names, and a resolver for a name known only at run time.

Readme

@aweftjs/icons

Icon sets as modules. Name an icon in your source and the build puts that one icon in your page; name one when the page runs and a resolver fetches it. This package holds no drawings of its own and never will.

npm install @iconify-json/lucide

The sets are optional peers, so nothing is installed until you pick one. Any published set works: @iconify-json/lucide, @iconify-json/mdi, @iconify-json/tabler, @iconify-json/svg-spinners.

Naming an icon in your source

import { Icon } from '@aweftjs/ui';

<Icon name="lucide:check" label="done" />

@aweftjs/build rewrites that literal into an import of that one icon, a few hundred bytes (over Lucide: median 325, largest 978), and @aweftjs/icons generates the module it imports. Nothing else in the set reaches your page.

The rewrite fires for a string literal shaped set:name, on the name prop of the Icon your file bound from @aweftjs/ui, written as JSX or as an h(Icon, { ... }) call. Nothing else moves: a name with no colon in it, a value that is not a string literal, a spread that could carry its own name, and an Icon bound from anywhere else all come out as they went in and are looked up through Icons when the page runs.

You can write the import yourself, and it is the same module:

import check from '@aweftjs/icons/lucide/check';

<Icon name={check} />

Three subpaths exist, and all three are generated from the set you installed:

| import | what you get | |---|---| | @aweftjs/icons/lucide/check | one icon, as IconData | | @aweftjs/icons/lucide/+standard | the names ui's components ask for, as an IconPack | | @aweftjs/icons/lucide | the whole set as an IconPack, about 588 KB of it |

The + in +standard is deliberate: no set can publish a name with one in it, so that path can never be an icon you wanted.

They are not files, so they need aweft() in your bundler or the Node loader:

import { aweft } from '@aweftjs/build';
export default { plugins: [aweft()] };

and node --import @aweftjs/build/loader where you render a page without a browser. That is the same plugin and the same loader that compile your JSX; there is nothing extra to register.

A set named node could not be reached this way, because @aweftjs/icons/node is the generator's own subpath. No published set has that name.

The names your components ask for

Everything in @aweftjs/ui that shows an icon asks for a name, never a drawing: chevron-down, chevron-up, chevron-left, chevron-right, check, x, triangle-alert, search, upload. The list is standardIcons, exported by ui.

import { Icons } from '@aweftjs/ui';
import standard from '@aweftjs/icons/lucide/+standard';

<Icons value={standard}><App /></Icons>

That costs about ten icons rather than a set. To give one of those names a drawing of your own, put a pack in front:

<Icons value={[{ icons: { 'chevron-left': myOwnChevron } }, standard]}><App /></Icons>

The nearest source wins, so your pack answers and nothing else changes.

A spinner that moves

@aweftjs/ui ships no drawings and its default loader is three pulsing spans. For a drawn one, svg-spinners is a set like any other and its icons carry their own motion:

npm install @iconify-json/svg-spinners
import { Icon, LoaderContext } from '@aweftjs/ui';
import spinner from '@aweftjs/icons/svg-spinners/3-dots-fade';

<LoaderContext value={{ loading: () => <Icon name={spinner} size="1.25em" /> }}><App /></LoaderContext>

That one line covers every wait in ui: a suspend, a Button running a promise, and a FileDrop entry being uploaded.

Nothing special happens for an animated set. Icon writes an icon's body into the element as markup, so the <animate> elements inside these drawings are ordinary SVG animation the moment the page has them, on a fresh mount and on markup a server wrote. That is checked in ui's own suite, "an animated body is written through as it is".

A name you only have when the page runs

import { fromUrl } from '@aweftjs/icons';

<Icons value={[standard, fromUrl('https://api.iconify.design')]}><App /></Icons>

fromUrl(base) fetches <base>/<set>.json?icons=<name> and reads the answer the public icon APIs give. Point it at a route of your own to serve the icons yourself; recipes/icons/main.ts has that route in about fifteen lines, built out of an installed set.

Nothing installs it. A page that never adds it makes no requests, because a fetch to somebody else's service is a decision you make and not one a library makes for you.

It answers null for a name with no set in it, so check keeps being answered locally. A page whose server fetched an icon has to hand the client what the server got, as a pack: a hydration waits for nothing, so a resolver alone leaves the client one drawing short of the markup it is taking over.

A drawing that arrives this way is somebody else's markup, and Icon writes it into the page. So a body that can run or reach out is refused with unsafe-body, naming the icon: one carrying <script, an event attribute such as onload=, <foreignObject, a javascript: URL, or an href that does not begin with #. A <use href="#id"> and a fill="url(#id)" pass. The refusal is thrown, not answered as null, so the page hears which source refused and why rather than falling through to the next. An installed set is read at build time and is not read for this.

When something is missing

A set you have not installed:

set-not-installed: the icon set "tabler" is not installed; run: npm install @iconify-json/tabler.
Install the icon set the message names, or hand a pack of your own to Icons.

A name the set does not have is icon-not-in-set, naming both. Both happen while the page is being built, not while it is running, which is the point of naming an icon in your source.

A name nothing in your Icons stack answers is a loud assert from ui in development, and nothing at all in a release build. The full list of refusals is errors.txt.

What this package never decides

Which sets you install. Whether your page fetches anything. What an icon looks like. It ships no icon data, and nothing in the gate reads this package for drawings. npm run dependencies at the repo root checks something else, that no icon set is a dependency rather than an optional peer.