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

@webflow/webflow-cli

v2.8.0

Published

The Webflow CLI lets you manage Webflow sites, CMS content, forms, assets, and code components from the command line.

Downloads

328,913

Readme

@webflow/webflow-cli

The Webflow CLI lets you manage Webflow sites, CMS content, forms, assets, and code components from the command line.

Installation

npm install -g @webflow/webflow-cli

Commands


Authentication

Authenticate with Webflow and save credentials locally.

webflow auth login          # OAuth login — saves token to .env
webflow auth login --force  # Re-authenticate even if credentials exist

Sites

Manage Webflow sites from the CLI, and keep a project directory's local source files (page bodies, styles, variables) in sync with the Designer.

webflow sites list                           # List all sites in your workspace
webflow sites get <siteId>                   # Get details for a specific site
webflow sites domains --site <siteId>        # List custom domains for a site
webflow sites publish --site <siteId>        # Publish a site
webflow sites init                              # Bind this project to an existing site, or create a new one
webflow sites init --site-id <siteId>            # Bind without the interactive picker (CI/scripted setup)
webflow sites init --create --name "My Site"     # Create a new site and bind to it
webflow sites pull                                # Write the site's source files locally
webflow sites push                                # Push local changes back to the site
webflow sites push --dry-run                      # Preview what push would send

sites init, sites pull and sites push are in beta and ship only in @next builds (npm i -g @webflow/webflow-cli@next). They aren't registered in the stable release yet.

By default, pages materialize under src/pages/<slug>.tsw (the default page as index.tsw), components under src/components/<name>.tsw, and design-system theme modules under src/theme/. Styles and variables have no framework convention to follow, so they land at site/styles/ and site/variables/. A .webflow/wfs/ lockfile (one file per tracked path, plus an index.json) records which site the project is synced with and each file's last-synced hash, so push can detect conflicting Designer edits.

Set site.layout in webflow.json to use a different framework's layout:

{
  "siteId": "...",
  "site": {
    "layout": { "preset": "nextjs" }
  }
}

| Preset | Shape | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------- | | library/astro | Flat src/pages/<slug>.tsw + src/components/<name>.tsw (the default) | | nextjs | Next.js App Router — one folder per route segment, nested page.tsw (src/app/<slug>/page.tsw, home at src/app/page.tsw) | | nextjs-pages | Next.js Pages Router — flat pages/<slug>.tsw |

A page nested under a Designer Page Folder (e.g. /blog/en/about) mirrors that subpath locally (<pagesRoot>/blog/en/about.tsw, or .../blog/en/about/page.tsw under the nextjs shape). A component's Designer group maps to a local subfolder under componentsRootsrc/components/ui/Card.tsw and a flat src/components/Card.tsw are distinct components. Explicit pagesRoot/componentsRoot/pagesShape ("flat" or "nested") override a preset's defaults, or can be set without a preset at all. The theme root isn't configured directly — it's derived as a sibling of whatever pagesRoot and componentsRoot have in common, so src/pages + src/components puts theme modules at src/theme.

Pulled .tsw files are formatted with Prettier, honoring the project's own Prettier config when it has one.

A pull also writes reference material the site regenerates each time — site/README.md, WFDL typedefs under site/wfdl/, and site/assets.json. These are read-only: the site tells the CLI which paths it accepts writes to, and push skips the rest whether or not --force is passed. Edit them freely for local reference, but a pull will overwrite your changes and a push will never send them.

A project is bound to one site: the lockfile records which site it was pulled from, and both commands refuse to run against a different one. Use sites pull --site-id <other> --reset to repoint a project at another site — it discards the existing sync state and pulls from scratch.

sites list options

| Flag | Description | | ------------------- | ---------------------------------------------------------------------------------------------------------------- | | --fields <fields> | Comma-separated columns to display (id, displayName, shortName, timeZone, createdOn, lastUpdated, lastPublished) | | --json | Output as JSON |

sites publish options

| Flag | Description | | --------------------- | ---------------------------------------------------------- | | --site <siteId> | Site ID to publish | | --domain <domainId> | Publish to a specific custom domain (repeatable) | | --page <pageId> | Publish a single page instead of the full site | | --dry-run | Preview what would be published without triggering a build |

sites init options

| Flag | Description | | --------------------- | --------------------------------------------------------------- | | --site-id <siteId> | Skip the interactive picker and bind to this site ID | | --create | Create a new Webflow site instead of binding to an existing one | | --name <name> | Name for the new site (implies --create; skips the prompt) | | --api-token <token> | Use this token instead of the stored session |

sites pull options

| Flag | Description | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | --site-id <siteId> | Pull this site instead of the one in webflow.json, without rebinding the project. Needs --reset if the project is already synced with another site | | --force | Overwrite local files that changed both locally and in the Designer since the last sync (they're left alone by default) | | --reset | Discard the existing local sync state and pull from scratch | | --json | Output the pulled manifest and file list as JSON | | --api-token <token> | Use this token instead of the stored session |

sites push options

| Flag | Description | | --------------------- | ------------------------------------------------------------------------------------------------------------- | | --dry-run | List the files that would be pushed without sending them | | --force | Skip upstream conflict checks and re-push every writable tracked file, not just the ones that changed locally | | --json | Output the push results as JSON (adds a skipped object when files were left out) | | --api-token <token> | Use this token instead of the stored session |

When --json leaves files out, the envelope carries a skipped object alongside results (or alongside changes under --dry-run, which has no results key). It appears only when something was skipped, and carries just the keys that apply:

| Key | Shape | Meaning | | ------------------ | ------------------------------ | --------------------------------------------------------- | | readOnly | local paths | Read-only reference files with local edits | | missing | local paths | Tracked files no longer on disk | | stale | local paths | Leftover copies of files that now live elsewhere | | unmappable | local paths | Files that don't resolve to a pushable wire path | | orphanedUpstream | local paths | Files deleted in the Designer that still have local edits | | renameCandidates | { oldPath, newPath } objects | Files that look like a local rename |

renameCandidates holds objects while every other key holds plain path strings, so flattening Object.values(skipped) into one list of paths will not work.


CMS

Manage CMS collections and items.

# Collections
webflow cms collections list --site <siteId>
webflow cms collections get --site <siteId> --collection <collectionId>
webflow cms collections create --site <siteId> --name "Blog Posts" --slug "blog-posts"

# Fields
webflow cms fields create --collection <collectionId> --name "Author" --type PlainText

# Items
webflow cms items list --site <siteId> --collection <collectionId>
webflow cms items get --site <siteId> --collection <collectionId> --item <itemId>
webflow cms items create --site <siteId> --collection <collectionId> --data '{"name":"Hello"}'
webflow cms items update --site <siteId> --collection <collectionId> --item <itemId> --data '{"name":"Updated"}'
webflow cms items delete --site <siteId> --collection <collectionId> --item <itemId>
webflow cms items publish --site <siteId> --collection <collectionId> --item <itemId>

cms collections list options

| Flag | Description | | ------------------- | ---------------------------------------------------------------------- | | --sort-by <field> | Sort by: displayName, slug, lastUpdated (default: displayName) | | --order <dir> | asc or desc (default: asc) | | --json | Output as JSON |


Forms

List forms and export submissions from a Webflow site.

webflow forms list --site <siteId>                                           # List all forms on a site
webflow forms submissions --site <siteId> --form <formId>                    # List submissions in the terminal
webflow forms submissions --site <siteId> --form <formId> --output out.csv   # Export submissions to CSV

Note: Forms commands require the forms:read OAuth scope. If you authenticated before upgrading to v1.21.0, run webflow auth login --force to pick up the new scope.

forms list options

| Flag | Description | | ------------------- | ------------------------------------------------------------------------------------------------------------------------- | | --site <siteId> | Site ID (required) | | --fields <fields> | Comma-separated columns: id, displayName, name, slug, createdOn, lastUpdated (default: id,displayName,slug) | | --json | Output as JSON |

forms submissions options

| Flag | Description | | ----------------- | ---------------------------------------------------- | | --site <siteId> | Site ID (required) | | --form <formId> | Form ID (required) | | --output <file> | Save all submissions as CSV to the specified file | | --json | Output as JSON (ignored when --output is also set) |


Assets

Upload and manage site assets.

webflow assets list --site <siteId>                        # List assets
webflow assets upload <file> --site <siteId>               # Upload a file
webflow assets update <assetId> --display-name "New name"  # Update asset metadata
webflow assets folders --site <siteId>                     # List asset folders
webflow assets folders create --site <siteId> --name "Images"

assets list options

| Flag | Description | | --------------------- | --------------------------------------------------------------------------------------------------------- | | --folder <folderId> | Filter by folder | | --sort-by <field> | Sort by: displayName, originalFileName, contentType, size, lastUpdated (default: displayName) | | --order <dir> | asc or desc (default: asc) | | --fields <fields> | Comma-separated columns to display | | --limit <n> | Max assets to return | | --offset <n> | Skip N assets (for pagination) | | --json | Output as JSON |

assets upload options

| Flag | Description | | ---------------------- | ----------------------------------- | | --name <displayName> | Display name (defaults to filename) | | --folder <folderId> | Upload into a folder | | --dry-run | Preview without uploading |

Supported file types: PNG, JPG, JPEG, GIF, SVG, WEBP, AVIF (≤ 4 MB); PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, TXT, CSV, ODT, ODS, ODP, JSON, Lottie (≤ 10 MB)


DevLink Import (Code Components)

Build and import React component libraries to your Webflow workspace.

webflow devlink import    # Build and share your component library
webflow devlink bundle    # Build locally without sharing

Aliases: webflow library share, webflow library bundle

Prerequisites

Create a webflow.json in your project root:

{
  "library": {
    "name": "My Component Library",
    "description": "A collection of custom components",
    "components": ["src/components/**/*.webflow.tsx"]
  }
}

devlink import options

| Flag | Description | | --------------------- | --------------------------------------------------- | | --api-token <token> | Workspace API token (overrides WEBFLOW_API_TOKEN) | | --force | Compile even with warnings or type errors | | --dev | Bundle in development mode |

devlink bundle options

| Flag | Description | | ---------------------- | ----------------------------------------------------- | | --public-path <path> | Set the public path for the bundle | | --output-path <path> | Set the output path for the bundle (default ./dist) | | --force | Compile even with warnings or type errors | | --dev | Bundle in development mode |


DevLink Export

Export Webflow Components from your Webflow Site

webflow devlink export    # Export Webflow Components

devlink export options

| Flag | Description | | --------------------- | --------------------------------------------------- | | --api-token <token> | Workspace API token (overrides WEBFLOW_API_TOKEN) | | --site <siteId> | Site ID to export from |


Designer Extensions

Scaffold and serve Webflow Designer Extensions.

webflow extension init <name>   # Create a new extension project
webflow extension serve [port]  # Serve locally (default port: 1337)
webflow extension bundle        # Build and package for upload (outputs bundle.zip)

Apps (Webflow Cloud)

Manage Webflow Cloud app environment variables non-interactively. --app-id and --environment-id default from webflow.json (cloud.app_id / cloud.environment_id) when omitted.

webflow apps env-vars list --app-id <appId> --environment-id <envId>            # List variables
webflow apps env-vars set <key> <value> --app-id <appId> --environment-id <envId>   # Create or update (plaintext; for secrets use stdin/prompt below)
webflow apps env-vars set <key> --secret                         # Prompt (hidden) for a secret value
printf %s "$VALUE" | webflow apps env-vars set <key> --secret    # Read a secret from stdin
webflow apps env-vars delete <key> --app-id <appId> --environment-id <envId>   # Delete a variable
webflow apps env-vars import .env --app-id <appId> --environment-id <envId>    # Bulk import from a .env file
webflow apps env-vars import .env.secrets --secret               # Import every key as a secret

Secret values are encrypted server-side and are never displayed: reads return them masked, and set / import never echo values to stdout, logs, or telemetry.

For set, the value is optional: omit it to read from piped stdin or to be prompted with hidden input. Prefer this for secrets — passing a value as a command-line argument leaves it in your shell history and the process list (set warns when you do so with --secret).

env-vars list options

| Flag | Description | | ----------------------- | ------------------------------------------------------------------------------------------------- | | --app-id <id> | Cloud app ID (default: cloud.app_id in webflow.json) | | --environment-id <id> | Environment ID (default: cloud.environment_id in webflow.json) | | --fields <fields> | Comma-separated columns (key, isSecret, value, id, environmentId). value is excluded by default | | --json | Output as JSON |

env-vars set / delete / import options

| Flag | Description | | ----------------------- | ------------------------------------------------------------------ | | --app-id <id> | Cloud app ID (default: cloud.app_id in webflow.json) | | --environment-id <id> | Environment ID (default: cloud.environment_id in webflow.json) | | --secret | (set, import) Mark the variable(s) as an encrypted secret | | --dry-run | Preview without making any API calls | | --json | Output as JSON |

A plain set on an existing secret preserves its secrecy (it never silently downgrades a secret to plaintext). import reports a per-key created/updated/skipped/errors breakdown and exits non-zero if any key fails (e.g. a reserved or invalid name). The bulk import is capped at 100 variables.


Skills (agent skills)

Install Webflow's first-party agent skills into your coding agents, so an agent working in your repo knows how to use Webflow without being told each time.

These commands write outside the current project. A --global install writes into your coding agents' skill directories under your home directory. Two rules bound that: nothing is replaced or deleted unless the CLI wrote it and it has not been edited since (anything else needs --force), and --dry-run prints every path before a single byte is written. Run it first if you are unsure.

webflow skills install                          # Restore from a lockfile, or pick interactively
webflow skills install webflow-cms --dry-run    # Preview every path that would be written
webflow skills install webflow-cms --agent claude-code,cursor
webflow skills install --all-skills --all-agents --global
webflow skills list                             # What is installed, and where
webflow skills update --check                   # Report what would change; write nothing
webflow skills update
webflow skills remove webflow-cms --dry-run     # Preview exactly what is deleted

Scope: project or global

| Scope | Where skills go | Lockfile | | ------------------- | ------------------------------------------- | ---------------------------------------- | | Project (default) | Agent directories under the current project | webflow-skills-lock.json (commit this) | | Global (--global) | Agent directories under your home directory | skills-lock.json in the CLI config dir |

The project lockfile is meant to be committed: a teammate (or CI) runs webflow skills install with no arguments and gets the same skills at the same commit. list shows both scopes by default so a global install is never invisible.

Each skill's files are written once into a shared canonical store and linked into every selected agent's directory, so installing for twenty agents costs one copy. Symlinks are used by default, with an automatic per-target fallback to copying when the filesystem or platform refuses them; --copy forces copies everywhere.

Safety

install and update refuse to overwrite a skill directory unless the CLI wrote it and it has not been edited since — anything locally modified, or anything that exists without a matching lockfile entry, is reported as blocked and needs --force. --dry-run prints the exact absolute paths that would be written or deleted and makes no changes, which is the flag to reach for first when an agent chose the arguments.

skills install options

| Flag | Description | | --------------------- | ---------------------------------------------------------------------- | | -a, --agent <ids> | Target agent; repeatable, comma-separated, or all | | --all-agents | Install for every supported agent | | --all-skills | Install every skill in the catalog | | -g, --global | Install into your home directory instead of the current project | | -p, --project | Install into the current project (the default) | | --copy | Copy files instead of symlinking from the shared store | | --ref <branch\|sha> | Pin to a branch or 40-character commit SHA (default: main) | | --force | Replace destinations that were modified locally or installed elsewhere | | -y, --yes | Skip the confirmation prompt | | --dry-run | Print every path that would be written; change nothing | | --json | Output as JSON |

skills update options

| Flag | Description | | --------------------- | ------------------------------------------------- | | --check | Report what would change without writing anything | | -g, --global | Update the home-directory install | | -p, --project | Update the current project's install | | --ref <branch\|sha> | Re-pin to a different branch or commit SHA | | --force | Overwrite skills that were modified locally | | -y, --yes | Skip the confirmation prompt | | --dry-run | Preview without writing | | --json | Output as JSON |

skills list options

| Flag | Description | | ------------------- | ----------------------------------------------------------------------------- | | -g, --global | Only the home-directory install | | -p, --project | Only the current project's install | | -a, --agent <ids> | Only skills linked into these agents; repeatable and comma-separated | | --fields <fields> | Comma-separated columns (name, dir, description, status, mode, agents, scope) | | --sort-by <field> | Sort by name, dir, status, or scope (default: name) | | --order <dir> | asc or desc (default: asc) | | --json | Output as JSON |

status is computed offline by re-hashing what is on disk against what the lockfile recorded: up-to-date, modified (edited since install), missing (recorded but gone), or unmanaged (present with no lockfile entry). Checking for upstream changes needs the network and is skills update --check's job.

skills remove options

| Flag | Description | | ------------------- | ------------------------------------------------------------- | | -a, --agent <ids> | Only unlink from these agents; repeatable and comma-separated | | --all | Remove every installed skill for the scope | | -g, --global | Remove from the home-directory install | | -p, --project | Remove from the current project's install | | --force | Also remove skill directories that have no lockfile entry | | -y, --yes | Skip the confirmation prompt | | --dry-run | Print exactly what would be deleted; change nothing | | --json | Output as JSON |

Removing a skill from one agent unlinks it there and keeps the shared store as long as another agent still references it; the store directory is deleted once nothing does.

remove only deletes what the lockfile records. A skill directory with no lockfile entry was not installed by this CLI — it may belong to another tool, or be hand-written — so --all leaves it alone and names it in a warning, and naming it directly is refused. --force opts in to removing those too.


Global Options

Available on all commands:

| Flag | Description | | --------------------- | ------------------------------------------------------------------------------------------------------- | | --no-input | Disable interactive prompts (for CI/CD) | | --manifest <path> | Path to webflow.json (default: ./webflow.json) | | --skip-update-check | Skip checking for package updates | | --verbose | Show detailed output | | --region <us\|eu> | Webflow region to target. See Region targeting below. Beta (@next) only for now. |

JSON errors

Commands run with --json emit failures as one JSON document on stdout and exit non-zero. The error object always contains message and may add fields that callers can use without parsing prose:

{
  "error": {
    "message": "The filesystem schema changed.",
    "context": "Failed to push site files",
    "missingFlag": "site",
    "code": "SCHEMA_STALE",
    "diagnostics": [
      {
        "source": "wfs",
        "phase": "conflict",
        "code": "SCHEMA_STALE",
        "severity": "error",
        "message": "The filesystem schema changed.",
        "hints": [],
        "retryable": false
      }
    ]
  }
}

context, missingFlag, code, and diagnostics are omitted when they do not apply. WFS diagnostics include stable source, phase, code, severity, source position, hints, retryability, and recovery metadata when supplied by the API.

Retry-aware commands (currently apps reads) add a further set of fields for distinguishing why a command failed without parsing the message, matching the exit code (see Exit codes below):

{
  "error": {
    "message": "429 Too Many Requests",
    "httpStatus": 429,
    "retryable": true,
    "retryAfterMs": 12000,
    "attempts": 3,
    "requestId": "req_abc123"
  }
}
  • httpStatus — the API's response status, when the failure came from a request the CLI actually sent.
  • retryable — whether this class of failure is one a caller could reasonably retry later. Set when the failure came from the CLI's retry-aware path (i.e. also has attempts), and additionally on any 429 — including from a write command — since the rate limiter rejects the request before it reaches the route handler, so nothing was applied server-side. A write command's non-429 failures never set this, since retrying a non-idempotent write automatically isn't safe.
  • retryAfterMs — the wait, in milliseconds, before a retry is likely to succeed. Present only alongside retryable: true. A value derived from a server header is exact; one derived from the CLI's own backoff (no header present) is a lower bound, floored at the default policy's base backoff delay (1000ms), so it may be conservative.
  • attempts — how many HTTP attempts the CLI made before giving up (including the first).
  • requestId — a correlation id echoed by the API, for support requests. Not present on every failure — the general rate limiter can reject a request before the id is attached to the response.
  • authError — set on the CLI's own auth-resolution failures (no session, or a failed webflow auth login flow) rather than an API rejecting a token — there is no httpStatus to key off in that case, but the fix is identical either way: run webflow auth login. A missing --site in a no-input run is isUserError/missingFlag instead (exit 2), not authError, since the token can be valid — only the site is unresolved.

All of the fields above are omitted when they do not apply.

Exit codes

| Code | Meaning | | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 0 | Success (including an empty result) | | 1 | General/unspecified error | | 2 | User-recoverable error — bad input, a missing required flag, a resource locked by another process, and similar states the caller can address without a backend change | | 3 | Auth error — either the API rejected a token (httpStatus 401/403), or the CLI's own auth resolution failed before any request was made (authError). Either way, the fix is webflow auth login. | | 4 | Rate limited (httpStatus 429) | | 5 | Transport error — an exhausted 5xx/408 retry, or a network failure | | 130 | Interrupted (Ctrl-C) |

A script or agent that only checks $? -ne 0 sees no difference from prior CLI versions; one that specifically checked $? -eq 1 to mean "any failure" should switch to $? -ne 0 to keep matching every failure.

Region targeting

US and EU are fully isolated Webflow stacks with separate accounts — the region in force decides which hosts and OAuth application every command uses. Resolved in this order, highest priority first:

  1. --region <us|eu> flag
  2. WEBFLOW_REGION environment variable (the CI/CD equivalent of the flag)
  3. region field in webflow.json
  4. The region you last logged into (persisted in ~/.config/webflow/auth.json)
  5. us (default)
{
  "region": "eu"
}

Only one session is stored at a time. webflow auth login authenticates against the active region's OAuth application and makes that region the default for later commands that don't specify one; logging into a different region replaces the stored session and updates the default. webflow auth status shows the active region and whether it has a stored session. webflow auth logout clears the stored session but keeps the region as the default, so a later regionless webflow auth login returns to it.

This is currently gated to @next (beta) and local development builds — EU targeting isn't announced yet. webflow --region eu ... on a stable install returns "unknown option."


License

MIT