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

@nominalcrew/webflow-kit

v0.5.0

Published

Vite plugin and CLI for Webflow sites: local HTTP dev, staging JS+CSS sync on save, and R2 deploys.

Readme

@nominalcrew/webflow-kit

Vite plugin and CLI for Webflow site repos. Internal agency tooling, published publicly so projects can install it — the license does not grant third-party use.

It does not ship a CDN origin. Each site passes its own cdn (or PUBLIC_ASSET_URL in .env).

  • Dev: HTTP Vite on http://localhost:3000, then open the site’s Webflow staging URL with ?nc-env=dev so the site loader pulls local modules. JS and CSS are also rebuilt and uploaded to {name}/staging/ on start and on every save.
  • Deploy: upload every file in dist/ to R2 under {name}/staging/ or {name}/production/. Restrict extensions with upload.include.

This directory is the package root. Depend on it from each site; do not copy it into a site as source.

What a site gets

| Command in the site repo | What the kit does | | ------------------------ | -------------------------------------------------------------------------------------------------------------------------- | | pnpm dev (vite) | HTTP dev server, CORS, CSS HMR, full reload on JS, open WEBFLOW_STAGING_URL?nc-env=dev, upload the staging build on save | | pnpm deploy:staging | Build unminified + sourcemaps, upload every file in dist/ to {name}/staging/ | | pnpm deploy:production | Build minified, upload every file in dist/ to {name}/production/ |

pnpm dev uploads the build output to staging only (never production). Production still requires deploy:production. Disable the save-sync with WEBFLOW_SYNC_STAGING=false.

Add it to a site

pnpm add -D @nominalcrew/webflow-kit

Node.js 24+. Peer: Vite 8.

To work on the kit itself next to a site:

{
  "devDependencies": {
    "@nominalcrew/webflow-kit": "link:../webflow-kit"
  }
}

1. Site vite.config.js

Load .env before the plugin so process.env is filled. Secrets and the CDN origin stay in the site .env, never in this package.

import "dotenv/config";
import { defineConfig } from "vite";
import { webflowKit } from "@nominalcrew/webflow-kit";
import config from "./webflow.config.js";

export default defineConfig(({ mode }) => ({
  plugins: [
    webflowKit({
      mode,
      ...config,
      cdn: process.env.PUBLIC_ASSET_URL,
    }),
  ],
}));

cdn is the public origin of your asset bucket (no trailing slash), for example https://cdn.example.com. After a deploy, the CLI prints {cdn}/{name}/staging/bundle.js.

You can also set cdn in webflow.config.js. The value passed to webflowKit() wins; if both are omitted, PUBLIC_ASSET_URL is read from the environment.

2. Site webflow.config.js

name is the R2 folder and must match the loader data-project.

export default {
  name: "my-site",
  assets: {
    js: "bundle.js",
    css: "bundle.css",
  },
  environments: {
    staging: { path: "staging" },
    production: { path: "production" },
  },
};

3. Site .env

Copy .env.example from this repo into the site.

| Variable | Required | Role | | ---------------------- | ---------------------- | -------------------------------------------------------------------- | | PUBLIC_ASSET_URL | for public deploy URLs | CDN origin passed as cdn | | WEBFLOW_STAGING_URL | for pnpm dev | Published *.webflow.io URL; opened with ?nc-env=dev | | WEBFLOW_SYNC_STAGING | no | Default true. Set false to skip JS+CSS uploads during pnpm dev | | R2_ACCOUNT_ID | for deploy | Cloudflare account id | | R2_ACCESS_KEY_ID | for deploy | R2 access key | | R2_SECRET_ACCESS_KEY | for deploy | R2 secret | | R2_BUCKET | for deploy | Bucket name | | PROJECT_ID | no | Overrides name for the R2 prefix | | R2_STAGING_PREFIX | no | Defaults to staging | | R2_PRODUCTION_PREFIX | no | Defaults to production |

Shared across sites on the same account: R2 keys, PUBLIC_ASSET_URL. Per site: name and WEBFLOW_STAGING_URL.

If WEBFLOW_STAGING_URL is empty, Vite still starts and logs a warning. It does not open a browser.

4. Webflow

Site Settings → Custom Code → Head. Staging CSS first (static <link>, so the Designer canvas can load it), then the loader. Do not add bundle.js. Host the loader on your CDN. data-project must equal name:

<link
  rel="stylesheet"
  href="https://cdn.example.com/my-site/staging/bundle.css"
/>
<script src="https://cdn.example.com/loader.js" data-project="my-site"></script>

The <link> must come before the script. Publish the site once so .webflow.io exists, then put that URL in WEBFLOW_STAGING_URL.

5. Site package.json scripts

{
  "scripts": {
    "dev": "vite",
    "build:staging": "vite build --mode staging",
    "build:production": "vite build --mode production",
    "deploy:staging": "pnpm build:staging && webflow-kit deploy staging",
    "deploy:production": "pnpm build:production && webflow-kit deploy production"
  }
}

Daily loop (from the site repo)

pnpm dev
  1. Vite listens on http://localhost:3000.
  2. The browser opens {WEBFLOW_STAGING_URL}?nc-env=dev.
  3. The loader injects @vite/client and /src/js/main.js.
  4. Staging JS + CSS are uploaded ({name}/staging/bundle.*).
  5. Save CSS → HMR on the published tab, and staging is uploaded again. Refresh the Designer to see CSS in the canvas.
  6. Save JS → full page reload on the published tab, and staging JS + CSS are uploaded. .webflow.io without ?nc-env=dev then has the last save.

First run: if scripts fail, confirm Vite is running, then reload the Webflow tab.

pnpm deploy:staging      # QA / client on *.webflow.io (no ?nc-env=dev)
pnpm deploy:production   # live custom domain

R2 keys:

{name}/staging/bundle.js
{name}/staging/bundle.css
{name}/staging/assets/…
{name}/production/bundle.js
{name}/production/bundle.css
{name}/production/assets/…

Every file written to dist/ is uploaded, keeping its relative path. Omit upload.include to send all of them. Set it to allow only some extensions:

upload: {
  include: ["js", "css", "svg", "png", "webp", "woff2"],
}

Code-split chunks stay next to the entry on the CDN (./assets/… from bundle.js). Override the pattern with assets.chunks.

CLI (same thing, from the site cwd after a build):

webflow-kit deploy staging
webflow-kit deploy production

Plugin options

| Option | Default | Description | | ------------------------------ | ------------------------- | ------------------------------------- | | cdn | PUBLIC_ASSET_URL | Public CDN origin (no trailing slash) | | mode | from Vite | staging or production | | name | PROJECT_ID or project | R2 folder | | entry | ./src/js/main.js | JS entry | | outDir | dist | Build output | | assets.js | bundle.js | Uploaded JS filename | | assets.css | bundle.css | Uploaded CSS filename | | assets.chunks | assets/[name]-[hash].js | Code-split chunk filenames | | upload.include | all files | Allowed extensions, without the dot | | environments.staging.path | staging | Folder under {name}/ | | environments.production.path | production | Folder under {name}/ | | server | HTTP localhost:3000 | Vite server overrides |

Default server: host: true, strictPort: true, cors: true, HMR on ws://localhost:3000. Override via server in webflow.config.js if a site cannot use port 3000 — then set the loader data-dev-origin to match.

Publish

The package is on npm: @nominalcrew/webflow-kit. License: UNLICENSED (agency use only).

To release a new version, bump version in package.json, then:

npm login
pnpm publish --access public