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

html-paginator

v0.1.0

Published

Lightweight library for flexible page breaks in HTML printing using @media print

Readme

HTML Paginator

Dynamic content pagination library that intelligently distributes HTML content across page templates with custom headers, footers, and flexible layouts.

Features

  • Dynamic Content Distribution: Automatically flows content across multiple page templates
  • Custom Page Layouts: Each page can have unique headers, footers, and styling
  • Smart Element Splitting: Splits content at child element boundaries for natural flow
  • data-no-break Decorator: Keep specific elements intact across page boundaries
  • Height-Aware Pagination: Measures available space and distributes content intelligently
  • Zero Dependencies: Pure JavaScript with no external requirements
  • TypeScript Support: Full type definitions included
  • Cross-Environment: Works in both browser and Node.js (with JSDOM)

Installation

Method 1: NPM Install (Recommended)

npm install html-paginator

Then import in your JavaScript:

import { createPaginator } from 'html-paginator';

const paginator = createPaginator({
  pageTemplateSelector: '.page-template',
  bodyContentSelector: '.body-content',
  pageBodySelector: '.page-body'
});

paginator.paginate();

Method 2: Direct HTML Import (No Build Step)

Download the src/index.js file and include it directly in your HTML:

<!DOCTYPE html>
<html>
<head>
  <title>My Paginated Document</title>
</head>
<body>
  <!-- Your page templates and content here -->

  <script type="module">
    import { createPaginator } from './path/to/index.js';

    const paginator = createPaginator({
      pageTemplateSelector: '.page-template',
      bodyContentSelector: '.body-content',
      pageBodySelector: '.page-body'
    });

    paginator.paginate();
  </script>
</body>
</html>

Note: The type="module" attribute is required for ES6 imports.

Quick Start

HTML Structure

Create page templates and content container:

<!DOCTYPE html>
<html>
<head>
  <style>
    /* Hide original page templates */
    body > .page-template {
      display: none;
    }

    .page {
      width: 8.5in;
      min-height: 11in;
      background: white;
      margin: 20px auto;
    }

    .page-header {
      background: #333;
      color: white;
      padding: 20px;
    }

    .page-body {
      padding: 40px;
      min-height: 9in;
      max-height: 9in;
    }

    .page-footer {
      background: #f5f5f5;
      padding: 15px;
      text-align: center;
    }
  </style>
</head>
<body>
  <!-- Page Templates (hidden by default) -->
  <div class="page-template page">
    <header class="page-header">
      <h1>Document Title</h1>
      <span>Page 1</span>
    </header>
    <div class="page-body"></div>
    <footer class="page-footer">Footer content</footer>
  </div>

  <div class="page-template page">
    <header class="page-header">
      <h1>Document Title</h1>
      <span>Page 2</span>
    </header>
    <div class="page-body"></div>
    <footer class="page-footer">Footer content</footer>
  </div>

  <!-- Content to Paginate -->
  <div class="body-content">
    <div data-no-break>
      <h2>Section 1</h2>
      <p>This section stays together...</p>
    </div>

    <div>
      <h2>Section 2</h2>
      <p>This section can be split...</p>
      <p>Content continues...</p>
    </div>
  </div>

  <!-- JavaScript -->
  <script type="module">
    import { createPaginator } from './path/to/index.js';

    const paginator = createPaginator({
      pageTemplateSelector: '.page-template',
      bodyContentSelector: '.body-content',
      pageBodySelector: '.page-body',
      noBreakAttribute: 'data-no-break'
    });

    // Perform pagination
    paginator.paginate();

    // Get paginated pages
    const pages = paginator.getPages();
    console.log(`Created ${pages.length} pages`);
  </script>
</body>
</html>

How It Works

  1. Page Templates: Define the structure of each page (header, body area, footer)
  2. Body Content: Contains the content items to be distributed
  3. Distribution: The paginator measures available space in each page body and fills it with content
  4. Smart Splitting:
    • Elements with data-no-break stay intact
    • Elements without it can be split at the child level
  5. Overflow Handling: Last page gets remaining content, even if it overflows

API

createPaginator(options)

Creates a paginator instance with configuration options.

const paginator = createPaginator({
  pageTemplateSelector: '.page-template',  // CSS selector for page templates
  bodyContentSelector: '.body-content',     // CSS selector for content container
  pageBodySelector: '.page-body',           // CSS selector for page body area
  noBreakAttribute: 'data-no-break',        // Attribute for non-breaking elements
  pageContainerSelector: '.page-container', // Container for paginated pages
  hideEmptyPages: true                      // Hide unused page templates
});

Paginator Methods

paginate()

Performs pagination and renders the result.

paginator.paginate();

Returns: Paginator (for chaining)

getPages()

Returns array of paginated page elements.

const pages = paginator.getPages();
console.log(`Total pages: ${pages.length}`);

Returns: HTMLElement[]

reset()

Resets pagination and shows original content.

paginator.reset();

Returns: Paginator (for chaining)

setOptions(newOptions)

Updates configuration options.

paginator.setOptions({ hideEmptyPages: false });

Returns: Paginator (for chaining)

getOptions()

Gets current configuration.

const options = paginator.getOptions();

Returns: PaginatorOptions

Usage Examples

Report Generation

<style>
  .page {
    width: 8.5in;
    min-height: 11in;
    background: white;
    padding: 0;
  }

  .page-header {
    background: #333;
    color: white;
    padding: 20px;
  }

  .page-body {
    padding: 40px;
    min-height: 9in;
  }

  .page-footer {
    background: #f5f5f5;
    padding: 15px;
    text-align: center;
  }
</style>

<div class="page-template page">
  <header class="page-header">
    <h1>Annual Report 2024</h1>
  </header>
  <div class="page-body"></div>
  <footer class="page-footer">Page 1 - Confidential</footer>
</div>

<div class="body-content">
  <div data-no-break>
    <h2>Executive Summary</h2>
    <p>This section stays together...</p>
  </div>

  <div>
    <h2>Financial Data</h2>
    <p>Q1 results...</p>
    <p>Q2 results...</p>
    <p>Q3 results...</p>
    <p>Q4 results...</p>
  </div>
</div>

<script type="module">
  import { createPaginator } from 'html-paginator';

  const paginator = createPaginator();
  paginator.paginate();

  // Optional: Print the result
  window.print();
</script>

Invoice with Multiple Pages

<div class="page-template invoice-page">
  <header class="invoice-header">
    <div>Company Logo</div>
    <div>Invoice #12345</div>
  </header>
  <div class="page-body"></div>
  <footer class="invoice-footer">
    <p>Terms and conditions apply</p>
  </footer>
</div>

<div class="body-content">
  <!-- Line items that flow across pages -->
  <div class="line-item">Item 1 - $100</div>
  <div class="line-item">Item 2 - $200</div>
  <!-- ... many more items ... -->

  <!-- Total section stays together -->
  <div class="total-section" data-no-break>
    <h3>Total</h3>
    <p>Subtotal: $1000</p>
    <p>Tax: $100</p>
    <p>Total: $1100</p>
  </div>
</div>

Different Page Layouts

<!-- First page with cover design -->
<div class="page-template cover-page">
  <header class="cover-header">
    <h1>Document Title</h1>
    <p>Author Name</p>
  </header>
  <div class="page-body"></div>
</div>

<!-- Standard pages -->
<div class="page-template standard-page">
  <header class="standard-header">Page Header</header>
  <div class="page-body"></div>
  <footer class="standard-footer">Page 2</footer>
</div>

<div class="page-template standard-page">
  <header class="standard-header">Page Header</header>
  <div class="page-body"></div>
  <footer class="standard-footer">Page 3</footer>
</div>

The data-no-break Attribute

Use data-no-break on elements that should stay together on a single page:

<div class="body-content">
  <!-- This entire section stays together -->
  <div data-no-break>
    <h2>Important Notice</h2>
    <p>This content must not be split across pages.</p>
    <ul>
      <li>Point 1</li>
      <li>Point 2</li>
    </ul>
  </div>

  <!-- This section can be split at child level -->
  <div>
    <h2>Regular Content</h2>
    <p>Paragraph 1 might go on page 1...</p>
    <p>Paragraph 2 might go on page 2...</p>
  </div>
</div>

When to use data-no-break:

  • Signature blocks
  • Tables or charts
  • Important notices
  • Code blocks
  • Terms and conditions sections
  • Any content that loses meaning when split

Print Styles & PDF Generation

Remove Browser Headers/Footers

To use only your page-template headers/footers (not browser defaults), add these print styles:

/* Remove browser's default headers/footers */
@page {
  size: letter;
  margin: 0;
}

@media print {
  html, body {
    background: white;
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
  }

  .page-container {
    gap: 0;
  }

  .page {
    page-break-after: always;
    margin: 0;
    width: 100%;
    height: 100vh;
    box-shadow: none;
  }

  .page:last-child {
    page-break-after: auto;
  }

  /* Hide controls and original content */
  button, .controls, .no-print {
    display: none;
  }
}

Browser Print Settings

When printing or saving as PDF, configure your browser:

Chrome/Edge:

  1. Open Print dialog (Ctrl+P / Cmd+P)
  2. Click "More settings"
  3. Uncheck "Headers and footers"
  4. Set Margins to "None"
  5. Enable "Background graphics" (to show your custom headers/footers)

Firefox:

  1. File → Page Setup
  2. Margins & Header/Footer tab
  3. Set all headers/footers to "blank"
  4. Set margins to 0

Safari:

  1. File → Print
  2. Show Details
  3. Uncheck headers and footers
  4. Set margins to minimum

The @page { margin: 0; } rule should automatically remove browser headers/footers in most modern browsers, but manual configuration may be needed for some browsers.

TypeScript Support

Full TypeScript definitions included:

import { createPaginator, PaginatorOptions, Paginator } from 'html-paginator';

const options: PaginatorOptions = {
  pageTemplateSelector: '.page-template',
  bodyContentSelector: '.body-content',
  pageBodySelector: '.page-body',
  noBreakAttribute: 'data-no-break',
  hideEmptyPages: true
};

const paginator: Paginator = createPaginator(options);
paginator.paginate();

const pages: HTMLElement[] = paginator.getPages();

Advanced Usage

Dynamic Page Creation

// Create pages dynamically based on content
const contentSections = document.querySelectorAll('.section');
const pagesNeeded = Math.ceil(contentSections.length / 3);

for (let i = 0; i < pagesNeeded; i++) {
  const page = document.createElement('div');
  page.className = 'page-template';
  page.innerHTML = `
    <header>Page ${i + 1}</header>
    <div class="page-body"></div>
    <footer>Footer</footer>
  `;
  document.body.appendChild(page);
}

const paginator = createPaginator();
paginator.paginate();

Programmatic Content Addition

const bodyContent = document.querySelector('.body-content');

// Add items programmatically
for (let i = 0; i < 100; i++) {
  const item = document.createElement('div');
  item.textContent = `Item ${i + 1}`;

  // Mark every 10th item as non-breaking
  if (i % 10 === 0) {
    item.setAttribute('data-no-break', '');
  }

  bodyContent.appendChild(item);
}

const paginator = createPaginator();
paginator.paginate();

Browser Support

Works in all modern browsers that support:

  • ES6+ JavaScript
  • DOM manipulation APIs
  • getComputedStyle and layout measurement

Development

# Install dependencies
npm install

# Run tests
npm test

# Build
npm run build

# Test in watch mode
npm run dev

License

MIT

Contributing

Contributions welcome! Please open an issue or submit a pull request.