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

@joycostudio/iris

v0.0.1

Published

Analyze images and deliver the right version for each screen

Readme

Iris

Iris turns image sources and usage context into a loading plan. It has three parts:

  • Prepare assets: Find image files and hardcoded URLs in your code, inspect their metadata, then generate a catalog and small placeholders.
  • Define a policy: Recipes describe loading stages. Rules choose a recipe using image metadata and how the image is used.
  • Render the plan: The Next.js adapter applies the policy with Next Image. The core policy can also drive other renderers.

Next.js remains responsible for responsive sizes, optimization, and the final image request.

Get started

Requires Node.js 22.18+, Next.js 16+, and Sharp for image generation.

1. Install and initialize

npm i @joycostudio/iris
npm i -D sharp
npx iris init

2. Set up iris.config.ts

import { createIris } from "@joycostudio/iris"

export default createIris({
  assets: ["public/images/**/*.{png,jpg,jpeg,webp,avif,gif}"],
  recipes: {
    direct: { preview: "none" },
    blur: { placeholder: "blur" },
    staged: { placeholder: "blur", preview: { ratio: 0.5 } },
  },
  rules: [
    { when: { priority: true }, recipe: "direct" },
    { include: "/images/gallery/**", recipe: "staged" },
  ],
  fallback: "blur",
})

export const generate = {
  output: "src/generated/iris",
  scan: ["src/**/*.{js,jsx,ts,tsx}", "app/**/*.{js,jsx,ts,tsx}"],
}

assets catalogs files in public/images, including images no page uses yet. scan finds image URLs already written in your JS/TS files, including JSX like <Image src="/images/hero.png" />. The first matching rule chooses a recipe; fallback covers everything else.

3. Generate and use it

npx iris generate
import { IrisImage } from "@/generated/iris/image"

<IrisImage src="/images/hero.png" alt="Hero" fill sizes="100vw" priority />
<IrisImage src="/images/gallery/photo.jpg" alt="Photo" width={1200} height={800} />

Use regular Next Image props. Run iris generate again when image sources or placeholder encoding settings change. An image missing from the catalog renders as ordinary Next Image.

Recipe and rules

Recipes

A recipe is a named loading behavior. It controls what appears while the final image loads; Next.js still chooses and requests the final image.

| Recipe in the example | Definition | Result | | --------------------- | -------------------------------------------------- | ------------------------------------------------------- | | direct | { preview: "none" } | Final image, without an Iris preview | | blur | { placeholder: "blur" } | Tiny blurred placeholder, then final image | | staged | { placeholder: "blur", preview: { ratio: 0.5 } } | Placeholder, optional smaller preview, then final image |

Define each recipe under recipes and refer to it by name in a rule or fallback. A recipe can also be a synchronous function when its behavior depends on the image:

recipes: {
  adaptive: ({ asset, usage }) => ({
    placeholder: "blur",
    preview: asset.bytes > 500_000 && !usage.priority ? { ratio: 0.5 } : "none",
  }),
}

placeholder accepts "none", "blur", or blur settings. preview accepts "none" or settings such as ratio, maxWidth, and quality.

Rules

A rule says when to use a named recipe. When IrisImage renders, Iris tests rules using catalog metadata and image props. It checks from top to bottom and uses the first match. If none match, it uses fallback. The recipe prop on one IrisImage overrides this selection.

rules: [
  { when: { priority: true }, recipe: "direct" },
  {
    include: "/images/gallery/**",
    when: { sourceBytes: { min: 500_000 } },
    recipe: "staged",
  },
  { when: ({ asset }) => asset.alpha === true, recipe: "direct" },
]

Here, a priority gallery image uses direct because that rule comes first. A nonpriority gallery image over 500 KB uses staged; an image matching no rule uses fallback.

Each rule can have include and exclude filters for the source URL, plus a when condition. They must all pass for the rule to match. when can be an object of conditions, as above, or a synchronous function. include and exclude accept strings/globs, regular expressions, functions, and arrays. Recipe callbacks and rule predicates receive the same context:

| Field | What it contains | | ---------- | ------------------------------------------------------------------------------------------- | | src | The image URL | | asset | Catalog metadata for the original image, such as width, bytes, format, and alpha | | variants | Known image variants plus the original | | usage | Render context, such as width in CSS pixels, dpr, priority, tags, and custom data |

Object conditions include priority, sourceBytes, sourceWidth, renderedWidth, targetWidth, format, and alpha. Size conditions use inclusive { min?, max? } ranges. renderedWidth comes from usage.width (or a fixed width prop); targetWidth is that width multiplied by usage.dpr (default 1). For a fill image, pass usage={{ width: 800 }} if a rule needs its rendered width.

Common project needs

| Need | Use | | -------------------------------------------------------- | ---------------------------------------------------- | | Find hardcoded image URLs in app code | generate.scan | | Include all images in a folder | assets with a public/** glob | | Skip tests, fixtures, or specific URLs | generate.exclude | | Add CMS or remote image URLs | generate.sources or an async generate() function | | Choose a recipe by priority, image size, format, or path | rules with when or include | | Inspect generated assets and policy results | generate.report: true |

scan recognizes literal PNG, JPEG, WebP, AVIF, and GIF URLs, including full remote URLs. It cannot evaluate variables, imported assets, or interpolated paths; add those through assets or generate.sources. Remote images must be reachable when you run iris generate. Scanning does not rewrite your code: use IrisImage where you want the policy applied.

Reference

See the full reference for configuration options, loading behavior, other renderers, and debugging.

Maintainers

See releasing Iris to npm.