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

n8n-nodes-framer-images

v0.4.0

Published

n8n community node to upload images to Framer and get permanent CDN URLs

Readme

n8n Framer Custom Node (Docker)

Develop and run a custom n8n community node without a local Node.js install. Everything builds inside Docker.

Prerequisites

Quick start

# 1. Build the custom node inside Docker
docker compose --profile build run --rm node-build

# 2. Start n8n
docker compose up -d

# 3. Open n8n
start http://localhost:5678

Your Framer node supports CMS sync via the Framer Server API:

  • Get Collections — verify credentials and list CMS collections
  • Get Collection Fields — discover field IDs for mapping
  • Create or Update Item — upsert CMS content with draft/published status and optional site publish/deploy
  • Publish Document — write one item into existing CMS fields. Add a row per field. Formatted text fields receive Markdown or HTML, image fields receive a public URL, and plain fields receive the value as-is. Image URLs are not rewritten. Fields are not created.
  • Publish Strapi Entry — point a Strapi entry.publish or entry.unpublish webhook at this operation. Fields are created from the entry. Blocks become Markdown. Unpublish marks the Framer item as a draft. An optional Item ID overrides matching.
  • Sync Table to Collection — bulk 1:1 sync of all upstream records (e.g. full Airtable table) into a CMS collection

Credential setup

  1. In Framer, open Site Settings → General and create an API key
  2. Copy your project URL from the browser (e.g. https://framer.com/projects/Sites--abc123)
  3. In n8n, create a Framer API credential with both values
  4. Run Get Collections in a workflow to verify the connection

Field mapping (Airtable and other sources)

  1. Add Airtable → List Records and connect it to Framer → Create or Update Item
  2. Select your Collection and set Field Input Mode to Select Fields to Auto-Map
  3. Choose Field Sync Scope:
    • Selected Fields Only — add source column names under Source Fields (e.g. Title, Body). Only listed columns sync.
    • All Incoming Fields — syncs every top-level field from incoming items automatically.
  4. Field names are read from incoming $json.fields at runtime. Missing Framer fields are created automatically.
  5. Slug defaults to Auto from Source Field — reads the Slug Source Field column from $json.fields (default: Name) and slugifies it, e.g. Test2 → test2.

Airtable columns live under $json.fields. Column name → Framer field name, cell value → field content.

Airtable multipleAttachments image columns sync automatically in Select Fields to Auto-Map and Sync Table modes (first image wins). Removing all attachments in Airtable clears the Framer image field. Non-image-only attachment cells also clear the image field.

Sync whole table (Airtable → Framer)

For a full 1:1 mirror of an Airtable table into a Framer CMS collection:

  1. Add Airtable → List Records and connect it to Framer → Sync Table to Collection
  2. Select your target Collection
  3. Set Slug Source Field (default: Name) — used to generate URL slugs for Framer CMS items
  4. Leave Delete Orphaned Items enabled to remove Framer items no longer in Airtable (full mirror)
  5. Enable Delete Orphaned Fields to remove Framer CMS columns that are not in the Airtable table (schema mirror). Slug and system fields are always kept. This deletes stored data on all items for removed fields.
  6. For exact Airtable column order (including empty columns), set Field Source to From Airtable Table Schema and pass one upstream table schema object to Airtable Table Schema, e.g. {{ $('Table Schema').first().json }}
  7. Run the workflow — missing Framer fields are created automatically

Re-runs update existing items by Airtable record ID (stored in an auto-created Airtable ID field), so duplicate slug errors should not occur when data is unchanged. Rows removed from Airtable are deleted from Framer on the next sync.

Table schema format: pass a single table object with a fields array. A one-element array [{ ... }] is also accepted. A Code node upstream can extract one table from Get Base Schema if needed.

{
  "name": "Articles",
  "fields": [
    { "id": "fld1", "name": "Name", "type": "singleLineText" },
    { "id": "fld2", "name": "Body", "type": "multilineText" }
  ]
}

Safety: If upstream returns 0 records, nothing is deleted unless you enable Allow Empty Source to Clear Collection.

The node returns a single summary: created, updated, deleted, deletedFields, deletedFieldNames, skippedSchemaFields, fieldOrderApplied, syncedSourceFields, etc.

JSON field data (advanced)

Use Get Collection Fields first to find field IDs, then pass JSON like:

{
  "fieldId123": { "type": "string", "value": "My Title" },
  "fieldId456": { "type": "boolean", "value": true }
}

Development workflow

After changing TypeScript source files:

# Rebuild the node
docker compose --profile build run --rm node-build

# Restart n8n to load changes
docker compose restart n8n

Testing

All tests can run entirely inside Docker — no local Node.js install needed.

Docker commands (recommended)

| What | Command | |---|---| | Unit + mock integration (142 tests, no credentials) | docker compose -f docker-compose.test.yml run --rm test-unit | | Live Framer + n8n E2E (needs credentials in .env or .env.test) | docker compose -f docker-compose.test.yml --env-file .env.test run --rm test-e2e | | Full suite: build + unit + E2E + n8n smoke | See Full Docker test run below |

Shortcut npm scripts (still use Docker under the hood):

npm run test:docker          # unit + mock integration only
npm run test:docker:e2e      # live E2E (skipped if no FRAMER_* vars)

Full Docker test run

# 1. Copy and fill credentials (Framer required for live E2E; n8n API key optional for smoke tests)
copy .env.test.example .env.test

# 2. Build the node first (uses existing node-build service)
docker compose --profile build run --rm node-build

# 3a. Unit tests only — always works, no credentials
docker compose -f docker-compose.test.yml run --rm test-unit

# 3b. Live E2E against Framer sandbox
docker compose -f docker-compose.test.yml --env-file .env.test run --rm test-e2e

# 3c. Everything: starts n8n in Docker, rebuilds, runs unit + E2E
docker compose -f docker-compose.test.yml --env-file .env.test --profile test up -d n8n-test
docker compose -f docker-compose.test.yml --env-file .env.test --profile test run --rm test-full
docker compose -f docker-compose.test.yml --profile test down

Live E2E tests auto-skip when FRAMER_PROJECT_URL / FRAMER_API_KEY are unset — so test-unit always passes cleanly.

Local commands (only if you have Node.js installed)

| Command | What it runs | |---|---| | npm test | Unit + mocked integration tests | | npm run test:e2e | Live Framer + n8n smoke tests | | npm run test:coverage | Unit/integration with coverage |

Live E2E credentials

Copy .env.test.example to .env.test and set:

  • FRAMER_PROJECT_URL / FRAMER_API_KEY — Framer sandbox project (required for live sync tests)
  • FRAMER_TEST_COLLECTION_ID — optional dedicated test collection
  • N8N_API_KEY — optional; enables n8n smoke tests (K9/K10). Create one in n8n Settings → API.

For test-e2e without the test profile, point N8N_BASE_URL at your running n8n (http://host.docker.internal:5678 is the default).

Documented sync behaviors live in tests/BEHAVIOR.md.

Project layout

framer-node/
├── credentials/          # n8n credential types
├── nodes/Framer/         # Framer node source
├── dist/                 # Compiled output (generated, mounted into n8n)
├── docker-compose.yml    # n8n + node builder services
└── scripts/              # Build helpers

How it works

  • node-build — Uses node:22-alpine to run npm install and npm run build. No local Node.js required.
  • n8n — Official image docker.n8n.io/n8nio/n8n. Your compiled dist/ folder is mounted at /home/node/.n8n/custom and loaded via N8N_CUSTOM_EXTENSIONS.
  • n8n_data — Docker volume persists workflows, credentials, and settings across restarts.

Useful commands

# View n8n logs
docker compose logs -f n8n

# Stop everything
docker compose down

# Stop and remove persisted n8n data (fresh start)
docker compose down -v

Configuration

Copy .env values to adjust host, timezone, or webhook URL:

| Variable | Default | |---|---| | N8N_HOST | localhost | | N8N_PROTOCOL | http | | WEBHOOK_URL | http://localhost:5678/ | | GENERIC_TIMEZONE | Europe/Berlin |