Packery - Documentation

What is Packery?

Packery is a JavaScript library that makes it easy to create dynamic, responsive, and flexible layouts of items in a container. It’s particularly well-suited for layouts where items have varying sizes and you want them to fit neatly together without gaps. Think of layouts like a Pinterest board, an online portfolio, or a responsive image gallery. Packery uses a bin-packing algorithm to efficiently arrange items, making it performant even with a large number of elements. Unlike grid-based layouts, Packery adapts gracefully to different screen sizes and item dimensions, ensuring an optimal presentation across devices.

Key Features and Benefits

Comparison with Other Layout Libraries

Packery differs from other layout libraries like Masonry in its approach to bin-packing. While Masonry uses a column-based approach, Packery uses a more sophisticated algorithm that results in denser and often more aesthetically pleasing layouts, especially when dealing with irregularly sized items. Other libraries might focus on specific use-cases (like grids), while Packery offers flexibility to handle a wider range of layout scenarios. Its strength lies in handling dynamic content where item sizes are not known in advance or can change. Libraries such as Gridster focus on drag-and-drop functionalities which are not the primary function of Packery.

Setting up Packery: Installation and Dependencies

Packery can be easily integrated into your project using several methods:

<script src="https://unpkg.com/packery@<VERSION>/dist/packery.pkgd.min.js"></script>
npm install packery

Then, import it into your JavaScript file using a module bundler like Webpack or Parcel.

yarn add packery

Packery has a minimal dependency: it requires the existence of a querySelector method, which is widely supported across modern browsers. If you’re supporting very old browsers, you may need to include a polyfill for older browsers lacking this functionality. However, for most modern development this is not usually a concern. No other external libraries are required.

Getting Started with Packery

Basic Usage: Creating a Packery Layout

The simplest way to use Packery is to instantiate the Packery class with a container element. This element will hold the items you want to arrange. Here’s a basic example:

<div id="packery-container">
  <div class="item">Item 1</div>
  <div class="item">Item 2</div>
  <div class="item">Item 3</div>
  <!-- More items... -->
</div>
<script>
  const container = document.querySelector('#packery-container');
  const packery = new Packery( container, {
    // options...
  });
</script>

This code selects the container element with the ID “packery-container” and creates a Packery instance. The second argument is an optional options object (covered in the next section). The items within the container will be automatically laid out by Packery. Note that the item class is used here, but you can use any class name, as long as it’s consistent in your CSS and JavaScript.

Understanding the Packery Constructor

The Packery constructor takes two arguments:

  1. element (required): A DOM element or a selector string representing the container element where the layout will be created. This is where Packery will arrange the items.

  2. options (optional): An object containing various configuration options. This allows customization of the layout’s behavior, such as column width, item selector, and animation settings (discussed further below).

const packery = new Packery( '#myContainer', {
  //options
} );

The Packery constructor returns a Packery instance object which provides methods for manipulating and interacting with the layout.

Item Elements and Data

Packery arranges child elements within your container. By default, Packery assumes all direct child elements of the container are items. You can specify different items using the itemSelector option (see Essential Options and Configuration).

Packery does not inherently understand the content or semantics of each item. It only considers their dimensions in calculating the layout. You’ll use CSS to style items individually. You can add data attributes to your item elements to influence behavior or provide additional context for your own JavaScript interactions. For instance:

<div class="item" data-width="2" data-height="1">Item A</div>

This allows you to indicate that “Item A” should occupy a space twice as wide as a default item. You can access this data within your custom JavaScript functions.

Essential Options and Configuration

Several options allow you to customize Packery’s behavior:

Example using options:

const packery = new Packery( '#myContainer', {
  itemSelector: '.grid-item',
  columnWidth: 200,
  gutter: 10,
  percentPosition: true,
  transitionDuration: 0 // disables animation
});

Remember to consult the Packery documentation for the most up-to-date list of available options and their detailed explanations.

Working with Items

Adding and Removing Items

Packery provides methods to dynamically add and remove items from the layout.

const newItem = document.createElement('div');
newItem.className = 'item';
newItem.innerHTML = 'New Item';
packery.appended( newItem ); // Add single item

const newItems = [item1, item2, item3];
packery.appended( newItems ); // Add multiple items
const itemToRemove = document.querySelector('.item-to-remove');
packery.remove(itemToRemove);
const newItem = document.createElement('div');
newItem.className = 'item';
newItem.innerHTML = 'New Item';
packery.prepended( newItem );

Item Manipulation: Positioning and Resizing

While Packery automatically handles positioning, you can manually adjust item positions and sizes. Modifying an item’s size will trigger a layout update. For precise control, use the getItemElements method to get the underlying elements for specific items.

const item = packery.getItemElements()[0]; //Get the first item
item.style.width = '300px';
packery.layout();

Handling Item Events

Packery triggers several events that you can listen for to respond to changes in the layout. These events are dispatched on the Packery instance.

Example of listening for the layoutComplete event:

packery.on('layoutComplete', function() {
  console.log('Layout complete!');
});

Remember to use packery.off() to remove event listeners if necessary.

Working with Item Layouts

Packery provides several methods for working with item layouts:

Filtering and Sorting Items

While Packery doesn’t directly handle filtering and sorting, you can combine it with other JavaScript libraries or techniques to achieve this. You can hide and show items using CSS classes and then call layout() to update the Packery layout, effectively filtering items. For sorting, you would manipulate the DOM order of your items and then call layout() to refresh the layout. This approach gives you full control over how filtering and sorting are implemented.

Layout Options and Customization

Understanding Layout Modes

Packery primarily uses a bin-packing layout algorithm. It doesn’t offer distinct “modes” in the way some grid systems might (e.g., masonry, fitting, etc.). The core algorithm adapts to the sizes of the items you provide, aiming for the most efficient arrangement. However, the appearance of the layout is highly influenced by the configuration options you set (columnWidth, rowHeight, gutter, etc.), allowing you to achieve various visual effects.

Column and Row Configuration

The columnWidth and rowHeight options are central to controlling the layout. These options determine the grid-like structure that Packery uses as a basis for placement.

If neither columnWidth nor rowHeight is defined, Packery will attempt to determine an optimal layout based on the sizes of the items.

Controlling Item Sizes and Aspect Ratios

While Packery arranges items based on their dimensions, you control the dimensions themselves using CSS. You can set fixed sizes, percentages, or use flexible units like vw or vh to create responsive item sizes. Remember to call packery.layout() after making any changes to item sizes.

For greater control over item aspect ratios, consider using CSS techniques like padding-top with a percentage value. This can create the appearance of aspect ratio constraints without requiring explicit width and height settings. For example, to maintain a 16:9 aspect ratio:

.item {
  position: relative;
  padding-bottom: 56.25%; /* 9/16 = 0.5625 */
}
.item > img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

Customizing Gutter and Spacing

The gutter option controls the spacing between items. You can provide a single numeric value (pixels) for uniform horizontal and vertical spacing. For more granular control, provide an object:

const packery = new Packery( '#grid', {
  gutter: 10, // Uniform 10px gutter
  //or
  gutter: { horizontal: 20, vertical: 10 } //Different horizontal and vertical gutters
});

This allows you to have different spacing between columns and rows.

Responsive Layouts and Breakpoints

Packery itself isn’t directly tied to specific breakpoints. Its responsiveness comes from using CSS media queries to change item sizes, columnWidth, rowHeight, or other options based on screen size. Combine Packery with CSS media queries to adapt the layout to different screen sizes:

/* Styles for screens wider than 768px */
@media (min-width: 768px) {
  #packery-container {
    column-width: 200px;
  }
  .item {
    width: 100%; /*adjusting item size for bigger screens*/
  }
}

/* Styles for screens smaller than 768px */
@media (max-width: 767px) {
  #packery-container {
    column-width: 150px;
  }
    .item {
    width: 100%; /*adjusting item size for smaller screens*/
  }
}

Remember to call packery.layout() after resizing the window or triggering a media query change, to ensure the layout updates accordingly. For more complex scenarios, consider using a CSS framework with built-in breakpoint utilities.

Advanced Layout Options

Beyond the core options, consider exploring these:

Experimenting with these advanced options provides fine-grained control over layout behavior. Remember to consult the Packery documentation for the most up-to-date information on all options and their functionalities.

Advanced Techniques and Integrations

Integrating with Other Libraries (e.g., Isotope)

Packery works well in conjunction with other JavaScript libraries, particularly Isotope. Isotope is a filtering and sorting library that can be combined with Packery to create highly interactive and dynamic layouts. Isotope uses Packery (or other layout engines) as its underlying layout engine. Using Isotope allows you to filter and sort items, and Packery handles the re-layout efficiently.

To use Packery with Isotope:

  1. Include both Isotope and Packery in your project.
  2. Initialize Isotope with layoutMode: 'packery' and the Packery options you want to use.
$('#container').isotope({
  layoutMode: 'packery',
  packery: {
    columnWidth: 200,
    gutter: 10
  }
});

Isotope then handles filtering and sorting, and Packery manages the layout after each change.

Handling Animations and Transitions

Packery provides smooth animation during layout changes. The transitionDuration option controls the animation speed. Setting it to 0 disables animations. For more sophisticated animations, consider using CSS transitions or animation libraries like GSAP (GreenSock Animation Platform) to create custom animations synchronized with Packery’s layout updates. You can listen for Packery events like itemTransitionStart and itemTransitionEnd to trigger additional animations or effects.

Custom Item Renderers and Templates

Packery itself doesn’t provide a built-in templating engine. To create custom item renderers, you’ll need to use a templating library (like Handlebars, Mustache, or a simpler approach using JavaScript string manipulation) and integrate it into your workflow. Create your items using your templating engine, add them to the Packery container using the appended method, and call packery.layout() to update the layout.

Performance Optimization Techniques

For large datasets, optimizing Packery’s performance is crucial. Consider these techniques:

Troubleshooting and Debugging

Remember to carefully read the error messages in your browser’s developer console, as they often provide valuable clues to resolve issues. Debugging often involves a combination of careful code review, inspecting your HTML and CSS, and using the browser’s developer tools.

API Reference

Packery Constructor

The Packery constructor creates a new Packery instance.

new Packery( element [, options] )

The constructor returns a Packery instance object.

Methods

Events

Packery emits several custom events on its instance. You can listen for these using the on() method.

Example of listening for the layoutComplete event:

packery.on( 'layoutComplete', function( data ) {
  console.log( 'layout is complete', data );
});

Options

These options are passed as the second argument to the Packery constructor.

Remember to consult the official Packery documentation for the most current and detailed API reference, as specifics might change between versions.

Examples and Use Cases

Simple Grid Layouts

Packery excels at creating clean and efficient grid layouts, even with items of varying sizes. Here’s a basic example demonstrating a simple grid:

<div id="grid">
  <div class="grid-item">Item 1</div>
  <div class="grid-item">Item 2</div>
  <div class="grid-item">Item 3</div>
  <div class="grid-item wide">Item 4 (wider)</div>
  <div class="grid-item">Item 5</div>
</div>
<script>
  const grid = new Packery( '#grid', {
    columnWidth: 100,
    gutter: 10
  } );
</script>

In this example, columnWidth sets a base column width, and gutter adds spacing between items. The .wide class could be used with CSS to make certain items twice the width of standard items. Packery automatically arranges the items to fill the available space efficiently.

Masonry Layouts

While Packery doesn’t have a specific “masonry” mode, you can easily achieve a masonry-like effect by setting appropriate columnWidth and letting Packery’s bin-packing algorithm handle the layout.

<div id="masonry-grid">
  <!-- Items of various heights -->
</div>
<script>
  new Packery( '#masonry-grid', {
    columnWidth: 100,
    gutter: 10
  } );
</script>

The key here is to have items with varying heights; Packery will arrange them in a way that resembles a traditional masonry layout. Experiment with different columnWidth values to fine-tune the appearance.

Responsive Image Galleries

Packery is ideal for building responsive image galleries. Combine it with CSS media queries and percentage-based sizing to create galleries that adapt to different screen sizes:

<div id="image-gallery">
  <img src="image1.jpg" alt="Image 1">
  <img src="image2.jpg" alt="Image 2">
  <!-- More images -->
</div>
<script>
  const gallery = new Packery( '#image-gallery', {
    itemSelector: 'img',
    percentPosition: true, // Important for responsive layouts
    columnWidth: '.image-item' //Use a class selector for width
  } );
</script>

Here, percentPosition: true is crucial; it ensures that item positions are calculated as percentages, making the layout responsive. A class selector .image-item could be applied to the images, controlling their dimensions. Adjust the CSS based on screen size using media queries.

Dynamic Content Updates

Packery handles dynamic updates gracefully. Use the appended and prepend methods to add items, and remove to delete them. Remember to call packery.layout() after each update to refresh the layout.

// Add a new item
const newItem = document.createElement('div');
newItem.className = 'grid-item';
newItem.innerHTML = 'New Item';
grid.appended( newItem );
grid.layout();

// Remove an item
const itemToRemove = document.querySelector('.remove-me');
grid.remove( itemToRemove );
grid.layout();

This shows how to add and remove items, with grid.layout() ensuring the layout is recalculated.

Filtering and Sorting Examples

Packery itself doesn’t directly handle filtering and sorting. You need to combine it with another library (like Isotope) or manage this yourself by manipulating the DOM order of items and calling packery.layout().

A basic example of filtering (without a library) involves showing/hiding elements based on a criteria and then calling layout():

// Example Filter: hide items with class 'hidden'
const hiddenItems = document.querySelectorAll('.hidden');
hiddenItems.forEach(item => item.style.display = 'none');
grid.layout();

// Example Filter: show them back
hiddenItems.forEach(item => item.style.display = 'block');
grid.layout();

For more complex filtering and sorting, using Isotope is strongly recommended. Isotope provides a robust API for managing such operations seamlessly with Packery. Remember to consult Isotope’s documentation for details.