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

sanity-plugin-air

v0.1.0-alpha.1

Published

Integrate Air to your Sanity Studio.

Readme

sanity-plugin-air

Integrate Air into your Sanity Studio.

Architecture

This plugin uses an iframe architecture

┌─────────────────────────────────────────────────────────┐
│  Sanity Studio                                          │
│  ┌───────────────────────────────────────────────────┐  │
│  │  sanity-plugin-air (this package)                 │  │
│  │  - Installed in Sanity Studio                     │  │
│  │  - Renders an iframe                              │  │
│  │  - Communicates via postMessage                   │  │
│  │  ┌─────────────────────────────────────────────┐  │  │
│  │  │  <iframe src="sanity-plugin">               │  │  │
│  │  │  ┌─────────────────────────────────────────┐│  │  │
│  │  │  │  @apps/sanity-plugin (Next.js)          ││  │  │
│  │  │  │  - Full React app with auth, Tailwind   ││  │  │
│  │  │  │  - Uses parent.postMessage() to         ││  │  │
│  │  │  │    communicate with this plugin         ││  │  │
│  │  │  └─────────────────────────────────────────┘│  │  │
│  │  └─────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘

Features

  • Studio Tool: Browse your Air workspace from within Sanity Studio
  • Custom Field Type: Use air.asset fields to reference Air assets in your schemas

Getting Started (Link-Watch only)

Prerequisites

  • Node.js >= 18
  • A Sanity Studio project (v3) to test the plugin
  • Access to this monorepo

One-path setup (3 terminals)

Terminal 1: Plugin watch + link

cd plugins/sanity
yarn install        # first time
yarn link-watch     # builds, watches, and pushes updates via yalc

Terminal 2: Next.js iframe app

cd apps/sanity-plugin
yarn install        # first time
yarn dev            # runs at http://localhost:3005

Terminal 3: Your Sanity Studio

# once per project
yalc add sanity-plugin-air --link

yarn dev            # start Studio

sanity.config.ts:

import { airPlugin } from 'sanity-plugin-air'

export default defineConfig({
  // ...
  plugins: [
    airPlugin({ workspaceId: 'a valid workspace id' }),
  ],
})

You should now see:

  • An "Air" tool in the Studio navigation

Iteration loop

  1. Edit plugins/sanity/src/*
  2. yarn link-watch rebuilds and pushes automatically
  3. Refresh Studio to see changes

Configuration

airPlugin({
  appUrl: 'https://sanity-plugin.air.inc', // URL of the Air app (defaults to localhost:3005)
});

Scripts

| Script | Description | |--------|-------------| | yarn build | Build the plugin using current .env.local | | yarn build:dev | Build for local development (localhost:3005) | | yarn build:qa | Build for QA environment | | yarn build:prod | Build for production (sanity-plugin.air.inc) | | yarn dev | Watch mode - rebuilds on file changes | | yarn link-watch | Watch + push to yalc for live development | | yarn lint | Run Biome linter |

Environment Configuration

The plugin uses .env.local to configure the Air app URL at build time:

| File | Purpose | |------|---------| | .env.local | Active configuration (used by build) | | .env.local.bak | Development config (localhost:3005) | | .env.local.qa.bak | QA environment config | | .env.local.prod.bak | Production config (sanity-plugin.air.inc) |

The build:dev, build:qa, and build:prod scripts automatically copy the appropriate .bak file to .env.local before building.

Important: When publishing to npm, use yarn build:prod to ensure the production URL is baked into the bundle.


Troubleshooting

Plugin not showing in Sanity Studio

  1. Verify the plugin is installed: yarn why sanity-plugin-air
  2. Check sanity.config.ts includes airPlugin() in the plugins array
  3. Restart Sanity Studio

Iframe shows blank/error

  1. Ensure apps/sanity-plugin is running (yarn dev)
  2. Check appUrl in plugin config matches the running app URL
  3. Check browser console for CORS or other errors

Changes not reflecting

  1. Ensure yarn link-watch is running without errors
  2. Hard refresh Sanity Studio (Cmd+Shift+R)

Development Guide

Release Tags (Required)

All exported functions, types, and components must have a JSDoc release tag. The build will fail without them.

/** @public */
export function myPublicFunction() { ... }

/** @public */
export type MyPublicType = { ... }

/** @internal */
export function _internalHelper() { ... }

Available tags:

  • @public - Stable API, part of the public contract
  • @beta - May change in minor versions
  • @internal - Not for external use, prefix with underscore

The pkg-utils build --strict command enforces these tags at build time. If you forget a tag, you'll see an error like:

error: ae-missing-release-tag: "myFunction" is exported but missing a release tag

Linting

This package uses Biome for linting and formatting:

yarn lint              # Check for issues
yarn biome:check:write # Auto-fix issues

Package Structure

plugins/sanity/
├── src/
│   ├── index.ts           # Main exports (all need @public tags)
│   ├── plugin.tsx         # Plugin definition
│   ├── components/        # React components
│   ├── schema/            # Sanity schema definitions
│   ├── types/             # TypeScript types
│   └── utils/             # Utility functions
├── scripts/               # Build scripts (excluded from linting)
├── package.config.ts      # pkg-utils configuration
└── biome.json             # Linter configuration

Related

  • apps/sanity-plugin - The Next.js app that runs inside the iframe
  • packages/sanity/constants - Shared constants and types (@air/sanity-constants)
  • plugins/figma - Similar architecture for Figma plugin

License

BSD-3-Clause