React

Для интеграции библиотеки Packery в проект на React сначала необходимо установить её через npm:

npm install packery
npm install imagesloaded

imagesloaded часто используется вместе с Packery для корректного выравнивания элементов после загрузки изображений. В React важно помнить, что манипуляция DOM напрямую должна происходить через refs или эффекты (useEffect), чтобы избежать конфликтов с виртуальным DOM.

import React, { useEffect, useRef } from "react";
import Packery from "packery";
import imagesLoaded from "imagesloaded";

const Grid = ({ items }) => {
  const gridRef = useRef(null);

  useEffect(() => {
    const packeryInstance = new Packery(gridRef.current, {
      itemSelector: ".grid-item",
      gutter: 10,
      percentPosition: true
    });

    imagesLoaded(gridRef.current, () => {
      packeryInstance.layout();
    });

    return () => packeryInstance.destroy();
  }, [items]);

  return (
    <div ref={gridRef} className="grid">
      {items.map((item, index) => (
        <div className="grid-item" key={index}>
          {item.content}
        </div>
      ))}
    </div>
  );
};

export default Grid;

Основные параметры Packery

  • itemSelector – селектор элементов сетки. Обязательный параметр.
  • gutter – отступ между элементами в пикселях.
  • percentPosition – если true, позиционирование будет в процентах, полезно для адаптивных сеток.
  • originLeft / originTop – определяет направление укладки элементов.
  • stamp – позволяет «закрепить» элементы сетки, вокруг которых будут выстраиваться остальные.

Управление сеткой в React

Для динамического добавления или удаления элементов необходимо вызвать методы Packery через ref. Основные методы:

  • appended(element | elements) – добавляет новые элементы в сетку.
  • remove(element | elements) – удаляет элементы.
  • layout() – пересчитывает расположение всех элементов.
  • fit(element, x, y) – размещает элемент в сетке по координатам.
  • shiftLayout() – обновляет позиции элементов после изменений размеров контейнера.

Пример добавления нового элемента в React:

const addItem = (newItem) => {
  const element = document.createElement("div");
  element.className = "grid-item";
  element.innerText = newItem.content;

  gridRef.current.appendChild(element);
  packeryInstance.appended(element);
  packeryInstance.layout();
};

Адаптивная сетка

Для адаптивной работы сетки важно использовать percentPosition и обрабатывать события изменения размеров окна:

useEffect(() => {
  const handleResize = () => {
    packeryInstance.layout();
  };

  window.addEventListener("resize", handleResize);

  return () => window.removeEventListener("resize", handleResize);
}, []);

Использование Packery с изображениями

Packery корректно рассчитывает позиции только после того, как все изображения загружены. Для этого применяется библиотека imagesloaded:

imagesLoaded(gridRef.current, () => {
  packeryInstance.layout();
});

Если изображения загружаются динамически, необходимо повторно вызывать layout() после их добавления.

Комбинация с React Hooks и функциональными компонентами

Для удобного управления сеткой можно создать кастомный хук:

import { useEffect, useRef } from "react";
import Packery from "packery";
import imagesLoaded from "imagesloaded";

export const usePackery = (items, options = {}) => {
  const gridRef = useRef(null);
  const packeryRef = useRef(null);

  useEffect(() => {
    if (!gridRef.current) return;

    packeryRef.current = new Packery(gridRef.current, options);

    imagesLoaded(gridRef.current, () => {
      packeryRef.current.layout();
    });

    return () => packeryRef.current.destroy();
  }, []);

  useEffect(() => {
    packeryRef.current && packeryRef.current.layout();
  }, [items]);

  return gridRef;
};

Применение хука в компоненте:

const Grid = ({ items }) => {
  const gridRef = usePackery(items, { itemSelector: ".grid-item", gutter: 10 });

  return (
    <div ref={gridRef} className="grid">
      {items.map((item, index) => (
        <div className="grid-item" key={index}>
          {item.content}
        </div>
      ))}
    </div>
  );
};

Стилизация элементов

Для корректной работы Packery каждый элемент сетки должен иметь фиксированные размеры или размеры, вычисляемые через CSS. Пример базовой сетки:

.grid {
  width: 100%;
  position: relative;
}

.grid-item {
  width: 30%;
  margin-bottom: 10px;
  background: #f2f2f2;
  padding: 10px;
  box-sizing: border-box;
}

Использование процентов для ширины позволяет сетке адаптироваться к размеру контейнера.

Интерактивность и Drag & Drop

Packery совместим с библиотекой Draggabilly для перетаскивания элементов:

import Draggabilly from "draggabilly";

gridRef.current.querySelectorAll(".grid-item").forEach((item) => {
  const draggie = new Draggabilly(item);
  packeryInstance.bindDraggabillyEvents(draggie);
});

Это позволяет изменять расположение элементов интерактивно и динамически обновлять сетку.


Packery в связке с React обеспечивает гибкую, адаптивную и интерактивную сетку элементов. Управление через refs, useEffect и кастомные хуки позволяет сохранять совместимость с виртуальным DOM и упрощает динамическое добавление или удаление контента.