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

petersburg

v0.0.3

Published

React layout library implementing Hermitage-style 2D bin-packing for picture arrangement

Readme

Petersburg

A React layout library implementing Hermitage-style 2D bin-packing for picture arrangement.

Named after the Hermitage Museum in St. Petersburg, where curators arrange paintings tetris-style to maximize limited wall space.

Features

  • MaxRects bin-packing algorithm — efficient 2D rectangle packing
  • Multiple sort strategies — optimize for packing efficiency or preserve input order
  • Auto-height measurement — items can omit height; Petersburg measures rendered content
  • Responsive — auto-measures container and recalculates on resize
  • Accessible — DOM order matches visual flow for proper tab navigation
  • Lightweight — no dependencies beyond React

Installation

npm install petersburg

Basic Usage

import { HermitageLayout } from 'petersburg';

const items = [
  { id: '1', width: 200, height: 150, content: <img src="..." /> },
  { id: '2', width: 100, height: 100, content: <img src="..." /> },
  { id: '3', width: 150, height: 200, content: <img src="..." /> },
];

function Gallery() {
  return (
    <HermitageLayout
      items={items}
      gap={8}
      sortStrategy="ordered"
    />
  );
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | items | LayoutItem[] | required | Array of items to layout | | containerWidth | number | auto | Fixed width in pixels. If omitted, measures parent container. | | gap | number | 8 | Gap between items in pixels | | sortStrategy | SortStrategy | "none" | How to order items during packing | | className | string | — | CSS class for the container |

Types

interface LayoutItem {
  id: string;
  width: number;
  height?: number;  // Optional — if omitted, auto-measured from content
  content: ReactNode;
}

type SortStrategy =
  | "none"        // Keep input order
  | "height-desc" // Sort by height descending (best packing)
  | "ordered";    // Row 1 strict order, row 2+ flexible

Sort Strategies

none

Items are placed in input order using the MaxRects algorithm. Good for when your input is already sorted (e.g., by date) and you want to preserve that order as much as possible.

height-desc

Items are sorted by height (tallest first) before packing. This typically produces the most compact layout with minimal wasted space.

ordered

A hybrid approach for galleries where order matters at the top but efficiency matters overall:

  • Row 1: Items placed strictly left-to-right in input order
  • Row 2: Next batch of items, can be reordered within the row to fill gaps
  • Row 3+: Full algorithmic freedom for optimal packing

This is ideal for "newest items at top" layouts where the first row should show items 1, 2, 3... in order, but lower rows can be optimized.

Auto-Height Mode

For items with dynamic content (text cards, variable-length descriptions), omit the height property:

const items = [
  { id: '1', width: 200, content: <Card title="Short" /> },
  { id: '2', width: 200, content: <Card title="Much Longer Title Here" /> },
  { id: '3', width: 150, content: <Card title="Medium" description="With extra text" /> },
];

<HermitageLayout items={items} gap={8} />

Petersburg will:

  1. Render items invisibly to measure their natural height
  2. Pack using the measured dimensions
  3. Display the final layout

This adds a brief measurement phase but avoids content overflow or fixed-height constraints.

Responsive Layouts

Omit containerWidth to enable responsive mode:

<div style={{ width: '100%' }}>
  <HermitageLayout items={items} gap={8} />
</div>

The component will measure its parent container and recalculate the layout when the container resizes.

Fixed Width

For fixed-width layouts, provide containerWidth:

<HermitageLayout
  items={items}
  containerWidth={800}
  gap={8}
/>

Styling Items

Each item is rendered in an absolutely-positioned wrapper. Style your content to fill it:

const items = [
  {
    id: '1',
    width: 200,
    height: 150,
    content: (
      <img
        src="..."
        style={{ width: '100%', height: '100%', objectFit: 'cover' }}
      />
    ),
  },
];

Animations

Petersburg intentionally doesn't include animations to stay lightweight. Add your own with CSS transitions:

.my-gallery img {
  transition: transform 0.3s ease, opacity 0.3s ease;
}

Or use your preferred animation library on the item content.

How It Works

Petersburg uses the MaxRects bin-packing algorithm:

  1. Start with the full container as free space
  2. For each item, find the best position (topmost, then leftmost)
  3. Place the item and split the remaining free space into new rectangles
  4. Prune redundant free rectangles
  5. Repeat until all items are placed

This produces efficient layouts where items fill gaps left by differently-sized neighbors.

License

MIT