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

next-campaign-page-kit

v0.0.9

Published

Campaign site generator for Next Commerce campaigns.

Readme

Next Campaign Page Kit

CI npm version codecov

Next Campaign Page Kit is a tool for building campaign funnels that can be hosted with any of your favorite static site hosting providers such as Netlify or Cloudflare Pages.

Motivation

Most static site generators are designed around a single site. When you need to manage multiple campaign funnels in one repository, you quickly run into problems: shared layouts bleed across campaigns, assets collide, and a change to one campaign can silently break another.

Next Campaign Page Kit solves this by treating each campaign as a fully isolated unit within a single repository. Every campaign lives in its own subdirectory with its own layouts, assets, and configuration — but they're all built, versioned, and deployed together.

The CLI tools (dev, clone, config, compress) and template filters (campaign_asset, campaign_link, campaign_include) enforce this isolation at every step, so developers can work on one campaign without fear of affecting another.

Getting Started

1. Create a project directory

mkdir my-campaigns && cd my-campaigns

2. Initialize and install

npm init -y
npm install next-campaign-page-kit

3. Run the setup script

npx campaign-init

This will:

  • Add all CLI scripts to your package.json
  • Create _data/campaigns.json — empty campaign registry to get you started

4. Add your first campaign to _data/campaigns.json

Each entry is keyed by the campaign slug, which must match the campaign's directory name under src/.

{
  "my-campaign": {
    "name": "My Campaign",
    "description": "My first campaign",
    "sdk_version": "0.3.10"
  }
}

5. Create your campaign files

src/
└── my-campaign/
    ├── _layouts/
    │   └── base.html
    ├── assets/
    │   └── config.js
    └── presale.html

6. Set your Campaign API key

npm run config

[!IMPORTANT] Get your Campaign API key from the Campaigns App in your store. See Campaigns App Guide.

7. Start the development server

npm run dev

This will:

  1. Show a list of available campaigns
  2. Let you select which campaign to preview
  3. Start the dev server
  4. Open your browser to the selected campaign

By default the dev server starts on port 3000. You can configure the port with:

# Positional argument
npm run dev 8080

# Flag (requires -- to pass through npm)
npm run dev -- --port 8080
npm run dev -- -p 8080

# Environment variable
PORT=8080 npm run dev

Commands

| Command | Description | |---|---| | npm start | Interactive menu: dev server, compress, clone, configure | | npm run dev | Start dev server with interactive campaign picker | | npm run build | Build all campaigns to _site/ | | npm run clone | Clone an existing campaign to a new slug | | npm run config | Set the API key for a campaign | | npm run compress | Compress all images in a campaign directory | | npm run compress:preview | Preview compression savings without modifying files | | npm run migrate | Migrate campaigns.json from old array format to key-based format |

Build

Output will be in the _site directory:

npm run build

Clone Campaign

Clone an existing campaign to create a new one:

npm run clone

Campaign File Structure

your-project/
├── _data/
│   └── campaigns.json          # Campaign registry (contains data for all campaigns)
├── src/
│   └── [campaign-slug]/        # Individual campaign directory
│       ├── _layouts/           # Campaign-specific layouts
│       │   └── base.html       # Base layout template
│       ├── _includes/          # Reusable campaign components
│       ├── assets/             # Campaign assets (CSS, images, JS, config)
│       │   ├── css/            # Campaign styles
│       │   ├── images/         # Campaign images
│       │   ├── js/             # Campaign scripts
│       │   └── config.js       # SDK configuration
│       ├── presale.html        # Presale page (Base URL)
│       ├── checkout.html       # Checkout page
│       ├── upsell.html         # Upsell page
│       ├── receipt.html        # Receipt page
│       └── *.html              # Any other page
└── package.json

Key Files

  • _data/campaigns.json - Register all campaigns and their configuration data here. Uses a key-based format where each key is the campaign slug (see below). If you have an older project using the array format, run npm run migrate to convert it.
  • src/[campaign]/_layouts/base.html - Campaign's base layout
  • src/[campaign]/assets/config.js - Campaign Cart SDK configuration

Page Frontmatter

Each campaign page uses YAML frontmatter to configure the page for context.

Page Frontmatter Fields

| Field | Type | Required | Description | |-------|------|----------|-------------| | page_layout | string | No | Layout file in _layouts/. Defaults to base.html | | title | string | Yes | Page title for <title> tag | | page_type | string | Yes | Page type: product, checkout, upsell, receipt | | permalink | string | No | Custom URL path (e.g., /starter/) | | next_success_url | string | No | Redirect URL after successful checkout | | next_upsell_accept | string | No | URL when upsell accepted | | next_upsell_decline | string | No | URL when upsell declined | | styles | array | No | Page-specific CSS files (relative paths or external URLs) | | scripts | array | No | Page-specific JS files (relative paths or external URLs) | | footer | boolean | No | Show footer on this page |

Example

---
page_layout: base.html
title: Checkout
page_type: checkout
next_success_url: upsell.html
styles:
  - https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css
  - css/offer.css
scripts:
  - https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js
  - js/offer.js
footer: true
---

Campaign Context (campaign)

Each page automatically has access to its campaign's data from _data/campaigns.json via the campaign object. This allows you to provide configured context directly to your pages.

Usage

You can access any key defined in your campaign's entry in _data/campaigns.json:

<h1>{{ campaign.name }}</h1>
<p>Contact: {{ campaign.support_email }}</p>

Adding Custom Context

To add more context across all pages in your campaign, simply add new keys to your campaign in _data/campaigns.json:

{
  "starter": {
    "name": "Starter Campaign",
    "support_email": "[email protected]",
    "custom_headline": "Welcome to our Store!"
  }
}

Then the context is available to use it in your templates:

<h2>{{ campaign.custom_headline }}</h2>

Environment Variable

The environment variable is available in all templates and indicates the current build mode. This is useful for conditionally including analytics scripts, debug tools, or other environment-specific content.

| Command | Default Value | |---|---| | npm run dev | development | | npm run build | production |

Usage:

{% unless environment == "development" %}
  <!-- Google Tag Manager -->
  <script>...</script>
{% endunless %}

Override with CPK_ENV:

Set the CPK_ENV environment variable to override the default value. This is useful for build pipelines like Netlify or GitHub Pages where you may want a custom environment such as staging.

CPK_ENV=staging npm run build

Layout Resolution

Layouts are automatically resolved to the campaign's _layouts/ directory:

  • page_layout: base.htmlstarter/_layouts/base.html
  • page_layout: custom.htmlstarter/_layouts/custom.html

No layout specified? Defaults to base.html.

Template Tags (Filters)

Templates use Liquid syntax. Next Campaign Page Kit provides additional custom filters and tags for campaign-relative includes, assets, and links.

[!TIP] Use campaign template filters to ensure your includes, assets, and links are automatically handled when cloning templates to a fresh new campaign.

campaign_asset

Resolves asset paths to the current campaign.

Syntax:

{{ 'filename' | campaign_asset }}

Examples:

<!-- Config -->
<script src="{{ 'config.js' | campaign_asset }}"></script>
<!-- Output: /starter/config.js -->

<!-- CSS -->
<link href="{{ 'css/custom.css' | campaign_asset }}" rel="stylesheet">
<!-- Output: /starter/css/custom.css -->

<!-- Images -->
<img src="{{ 'images/logo.png' | campaign_asset }}" alt="Logo">
<!-- Output: /starter/images/logo.png -->

Use for: CSS files, JavaScript files, images, config.js, any campaign asset.


campaign_link

Generates clean URLs for inter-page navigation.

Syntax:

{{ 'filename.html' | campaign_link }}

Examples:

<!-- Navigation link -->
<a href="{{ 'checkout.html' | campaign_link }}">Checkout</a>
<!-- Output: /starter/checkout/ -->

<!-- Campaign Cart meta tag -->
<meta name="next-success-url" content="{{ next_success_url | campaign_link }}">
<!-- Output: /starter/upsell/ -->

<!-- Data attribute -->
<button data-next-url="{{ 'upsell.html' | campaign_link }}">Continue</button>
<!-- Output: /starter/upsell/ -->

Features:

  • Removes .html extension
  • Adds trailing slash
  • Prepends campaign slug
  • Handles anchor links (#section) and absolute URLs

Use for: Page links, navigation URLs, redirect URLs, Campaign Cart SDK meta tags.


campaign_include

Includes a file relative to the current campaign's _includes directory. This is useful for including reusable components that are specific to a campaign.

Syntax:

{% campaign_include 'filename.html' arg=value %}

Examples:

<!-- Include a slider component -->
{% campaign_include 'slider.html' images=slider_images %}

<!-- Include with parameters -->
{% campaign_include 'slider.html' images=slider_images show_package_image=true %}

Use for: Reusable components within a campaign (e.g., sliders, testimonials).

Connecting to Campaigns App

To connect this campaign to your 29 Next Campaigns App:

  1. Run npm run config
  2. Select your campaign
  3. Enter your API key from the Campaigns App
  4. Deploy your campaign

For more details, see the Campaigns App documentation.

Test Orders

You can use our test cards to create test orders.

Compress Images

Compress all images in a campaign directory in-place. Supports JPEG, PNG, WebP, and GIF. Only overwrites a file if the compressed output is smaller than the original.

npm run compress

This will:

  1. Show a list of available campaigns
  2. Let you select which campaign to compress
  3. Compress all images found anywhere in the campaign directory (src/[campaign]/)
  4. Print a before/after table with file sizes and total savings

Preview mode — see what would be saved without modifying any files:

npm run compress:preview

Example output:

◇  Found 3 images
◇  2 images ready to compress

--------------------------------------------+----------+----------+----------+-------+---------
 File                                       | Before   | After    | Saved    | %     | Status
--------------------------------------------+----------+----------+----------+-------+---------
 src/my-campaign/assets/images/hero.jpg     | 145.3 KB | 88.2 KB  | -57.1 KB | 39.3% | preview
 src/my-campaign/assets/images/product.png  | 80.0 KB  | 54.0 KB  | -26.0 KB | 32.5% | preview
--------------------------------------------+----------+----------+----------+-------+---------
 TOTAL                                      | 225.3 KB | 142.2 KB | -83.1 KB | 36.9% |
--------------------------------------------+----------+----------+----------+-------+---------

[NEXT] DEBUG 1 image already fully compressed, skipped
└  Preview complete — run without --preview to apply changes.

Already-optimized images are skipped and reported in the debug line above the summary.