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

@get-set/gs-flexogrid

v0.0.27

Published

Get-Set FlexoGrid

Readme

GSFlexoGrid

A dependency-free, responsive masonry/grid layout engine available in two flavours from one codebase:

  • Native / vanilla JS — a window.GSFlexoGrid(element, params) factory (also exposed as a jQuery plugin and an HTMLElement.prototype method).
  • React — a <GSFlexoGrid /> component.

Both share the exact same engine (actions/, constants/, helpers/, types/), so behaviour is identical across the two.

Features

  • Three layout modes: masonry (shortest-column packing), uniform (fixed-height tile grid), justified (rows stretched to fill the width, Flickr / Google-Photos style, honouring data-ar aspect-ratio hints)
  • Live sort (sortByOptions) and filter (filterByOptions) driven by your own buttons, with reset handlers
  • Relayout animation personalities: ease, spring, linear, none — with tunable duration
  • Staggered entrance reveal: fade, scale, fade-scale, slide, none — with per-item stagger cascade
  • Hover-lift (hoverLift) — lift + deepen the shadow on hover
  • Design tokens: gap, radius, shadow (built-in token shadow or a custom box-shadow string)
  • Theming: light, dark, auto colour themes via CSS custom properties
  • Shimmering loading skeleton (loading + skeletonCount)
  • Responsive breakpoints (responsive) — per-breakpoint param overrides, auto-sorted
  • Automatic relayout via ResizeObserver on the grid and on each item
  • Lifecycle hooks (beforeInit, afterInit, afterPositionAnimating)
  • All animations and the skeleton shimmer respect prefers-reduced-motion: reduce
  • React-only gsx prop for per-instance scoped styles

Compatibility

| Target | Requirement | |---|---| | React (the <GSFlexoGrid> component) | React 16.8+ (Hooks are required), and 17 / 18 / 19. React is an optional peer dependency — you only need it for the component. | | Native / vanilla (window.GSFlexoGrid) | No framework. Any modern evergreen browser (uses ResizeObserver). Optional jQuery ($(...).GSFlexoGrid(...)) and HTMLElement.prototype.GSFlexoGrid(...) adapters are registered automatically by the bundle. | | TypeScript | First-class — type declarations (.d.ts) ship in the package. | | SSR / Next.js | Safe to import server-side (no DOM access at module load). The grid itself is browser-only, so render it inside a Client Component ('use client'). |

The peer range is ^16.8.0 || ^17.0 || ^18.0 || ^19.0, with react/react-dom marked optional so the native build has zero React dependency.

Installation

npm i @get-set/gs-flexogrid

The package ships both targets:

  • React / npm entry: dist/components/GSFlexoGrid.js (main / module, types at dist/components/GSFlexoGrid.d.ts)
  • Native bundle (require / CDN): dist-js/bundle.js
  • Native stylesheet: styles/GSFlexoGrid.css

Project layout

GSFlexoGrid.ts             # native entry  (webpack -> dist-js/bundle.js, window global)
components/GSFlexoGrid.tsx # React component (tsc -> dist/, npm entry)
actions/                   # shared engine (init, calculatePositions, sort, filter, theme, skeleton, destroy)
constants/                 # shared constants (defaultParams, layoutTokens, propType)
helpers/uihelpers.ts       # shared helpers
types/                     # Params / Ref / Window augmentation
components/styles/         # compiled CSS + CSS-as-TS (runtime injection for React)
styles/                    # compiled CSS (for <link> use by the native build)

Build

npm install
npm run build          # builds both targets
npm run build:js       # native bundle -> dist-js/bundle.js
npm run build:react    # React + types  -> dist/

Tests

Unit tests use Vitest + jsdom:

npm test         # run once
npm run test:watch

Usage — Native JS

Include the stylesheet and the bundle, then call new GSFlexoGrid(element, params). Each direct child of the grid becomes a positioned item; tag items with data-* attributes to drive sorting/filtering.

<link rel="stylesheet" href="styles/GSFlexoGrid.css" />

<div class="grid">
  <div data-price="120" data-category="new"><img src="1.jpg" /></div>
  <div data-price="80"  data-category="sale"><img src="2.jpg" /></div>
  <div data-price="200" data-category="new"><img src="3.jpg" /></div>
</div>

<script src="dist-js/bundle.js"></script>
<script>
  new GSFlexoGrid(document.querySelector('.grid'), {
    count: 4,
    gap: '10px',
    layout: 'masonry',
    animation: 'spring',
    entrance: 'fade-scale',
    stagger: 40,
    hoverLift: true,
    theme: 'dark',
    radius: '12px',
    shadow: true,
    resetSortHandler: '.btn-reset-sort',
    resetFilterHandler: '.btn-reset-filter',
    sortByOptions: [
      { handler: document.querySelector('.btn-sort-date'), prop: 'date', type: 'string' },
      { handler: document.querySelector('.btn-sort-price'), prop: 'price', type: 'number' },
    ],
    filterByOptions: [
      { handler: document.querySelector('.btn-filter-new'), prop: 'category', value: 'new' },
    ],
    responsive: [
      { windowSize: 991, params: { count: 2 } },
      { windowSize: 767, params: { count: 1 } },
    ],
    afterInit: () => console.log('ready'),
  });
</script>

Equivalent jQuery / element forms (registered by the bundle when it loads):

// jQuery — initialises every matched element
$('.grid').GSFlexoGrid({ count: 3 });

// HTMLElement.prototype
document.querySelector('.grid').GSFlexoGrid({ count: 3 });

The registry & instance methods

Every instance is registered on the global window.GSFlexoGridConfigue registry under its reference key (auto-generated if you don't pass one). Look an instance up and call its methods:

new GSFlexoGrid(document.querySelector('.grid'), { reference: 'gallery' });

const instance = window.GSFlexoGridConfigue.instance('gallery');
instance.refresh(); // re-read children + recalculate layout
instance.destroy(); // disconnect observers, clear timers, strip inline styles, unregister

window.GSFlexoGridConfigue has the shape:

{
  references: Array<{ key: string; ref: Ref }>;
  instance: (key: string) => Ref | undefined; // returns undefined for unknown keys
}

The returned Ref exposes grid, currentParams, reference, data, originaldata, and the refresh() / destroy() methods.

Usage — React

import GSFlexoGrid from '@get-set/gs-flexogrid';
import { useRef } from 'react';

export default function Gallery() {
  const sortBtnRef   = useRef(null);
  const filterBtnRef = useRef(null);
  const resetRef     = useRef(null);

  return (
    <>
      <button ref={sortBtnRef}>Sort by price</button>
      <button ref={filterBtnRef}>Show new</button>
      <button ref={resetRef}>Reset</button>

      <GSFlexoGrid
        count={4}
        gap="10px"
        layout="masonry"
        animation="spring"
        entrance="fade-scale"
        stagger={40}
        hoverLift
        theme="dark"
        radius="12px"
        shadow
        resetFilterHandler={resetRef}
        sortByOptions={[
          { handler: sortBtnRef, prop: 'price', type: 'number' },
        ]}
        filterByOptions={[
          { handler: filterBtnRef, prop: 'category', value: 'new' },
        ]}
        responsive={[
          { windowSize: 991, params: { count: 2 } },
          { windowSize: 767, params: { count: 1 } },
        ]}
      >
        <div data-price="120" data-category="new"><img src="1.jpg" /></div>
        <div data-price="80"  data-category="sale"><img src="2.jpg" /></div>
      </GSFlexoGrid>
    </>
  );
}

The static stylesheet is injected into <head> automatically — no CSS import required. Each real child is tagged with the gs-flexogrid-item class so the radius / shadow / hover-lift tokens apply without extra markup. The dynamic options (layout, rowHeight, animation, duration, entrance, stagger, hoverLift, theme, radius, shadow, loading, skeletonCount, gap, count) are re-applied automatically whenever they change.

Next.js (App Router)

The grid is browser-only, so use it from a Client Component:

'use client';
import GSFlexoGrid from '@get-set/gs-flexogrid';

export default function Gallery() {
  return (
    <GSFlexoGrid count={4} gap="12px" layout="masonry">
      <div><img src="/1.jpg" /></div>
      <div><img src="/2.jpg" /></div>
      <div><img src="/3.jpg" /></div>
    </GSFlexoGrid>
  );
}

gsx — scoped styles (React only)

gsx is a React-only prop that injects per-instance, scoped CSS for this grid. It is a nested CSS-in-JS object (NestedCSS): each nested key becomes a descendant selector and the leaf values are CSSProperties. Rules are scoped to this instance via its generated data-key, so they never leak to other grids, and the injected <style> is removed automatically when the component unmounts.

<GSFlexoGrid
  count={3}
  gsx={{
    background: '#111',
    padding: '12px',
    '.gs-flexogrid-item': {
      borderRadius: '8px',
      overflow: 'hidden',
    },
    'img': {
      display: 'block',
      width: '100%',
    },
  }}
>
  {/* items */}
</GSFlexoGrid>

The example above injects (scoped to [data-key='...']):

[data-key='...'] { background: #111; padding: 12px; }
[data-key='...'] .gs-flexogrid-item { border-radius: 8px; overflow: hidden; }
[data-key='...'] img { display: block; width: 100%; }

Ref / imperative API (GSFlexoGridHandle)

The component is a forwardRef. Attach a ref to call instance methods directly:

import { useRef } from 'react';
import GSFlexoGrid, { GSFlexoGridHandle } from '@get-set/gs-flexogrid';

const gridRef = useRef<GSFlexoGridHandle>(null);

// <GSFlexoGrid ref={gridRef} count={4}>…</GSFlexoGrid>

gridRef.current?.refresh();  // re-read items + recalculate layout
gridRef.current?.destroy();  // tear down observers/timers + strip inline styles

| Method | Description | |---|---| | refresh() | Re-reads the grid's children and recalculates the layout (e.g. after items change). | | destroy() | Disconnects resize observers, clears pending layout timers, removes inline styles + token classes, and unregisters the instance. |

Options

Every option below is optional. Defaults come from constants/defaultParams.ts.

| Option | Type | Default | Description | |---|---|---|---| | reference | string | '' (auto GUID) | Unique key used by the window.GSFlexoGridConfigue registry. Auto-generated if omitted. | | count | number | 3 | Number of columns. | | gap | string | '' | CSS gap value, e.g. '10px' or '10px 20px' (row / column). | | className | string | '' | Extra CSS class added to the grid element. | | layout | 'masonry' \| 'uniform' \| 'justified' | 'masonry' | Layout algorithm. See Layout modes. | | rowHeight | number | 0 | Forced cell height (uniform) / target row height (justified) in px. 0 = auto (defaults to 220px internally). Ignored by masonry. | | animation | 'ease' \| 'spring' \| 'linear' \| 'none' | 'ease' | Relayout easing personality. See Animation modes. | | duration | number | 400 | Relayout transition duration in ms. | | entrance | 'fade' \| 'scale' \| 'fade-scale' \| 'slide' \| 'none' | 'fade-scale' | Reveal applied to items as they are first placed. See Entrance modes. | | stagger | number | 0 | Per-item delay (ms) for the entrance cascade. 0 = all at once. | | hoverLift | boolean | false | Lift + deepen the shadow on hover (adds gs-flexogrid--hover-lift). | | theme | 'light' \| 'dark' \| 'auto' | 'light' | Colour theme for the design-token variables. See Themes. | | radius | string | '' | Item corner-radius token, e.g. '12px' → sets --gs-flexo-radius. | | shadow | boolean \| string | false | true = built-in token shadow; a string = custom box-shadow; false = none. See Shadow. | | loading | boolean | false | Show the shimmering skeleton placeholder layer. | | skeletonCount | number | 0 | Number of skeleton tiles while loading. 0 = derive from count (count × 3). | | resetSortHandler | string \| HTMLElement \| RefObject | '' | Selector, element or React ref of a button that resets the active sort. | | resetFilterHandler | string \| HTMLElement \| RefObject | '' | Selector, element or React ref of a button that resets the active filter. | | sortByOptions | SortByOption[] | [] | Sort button definitions. See SortByOption. | | filterByOptions | FilterByOption[] | [] | Filter button definitions. See FilterByOption. | | responsive | ResponsiveOption[] | [] | Per-breakpoint param overrides (auto-sorted by windowSize descending). See Responsive. | | beforeInit | () => void | – | Callback fired before each position calculation. | | afterInit | () => void | – | Callback fired after init. | | afterPositionAnimating | () => void | – | Callback fired after the relayout transition ends. | | gsx | NestedCSS | – | React only — scoped, nested CSS-in-JS styles for this instance. See gsx. |

SortByOption

| Prop | Type | Description | |---|---|---| | handler | string \| HTMLElement \| RefObject | Button to attach the sort onclick to. | | prop | string | The data-* attribute name read off each item. | | type | 'string' \| 'number' | Determines the sort comparison ('number' sorts numerically). | | value | any | Optional — reserved; not required for sorting. |

FilterByOption

| Prop | Type | Description | |---|---|---| | handler | string \| HTMLElement \| RefObject | Button to attach the filter onclick to. | | prop | string | The data-* attribute name read off each item. | | value | any | Value to match against data-[prop]; matching items stay visible. | | type | 'string' \| 'number' | Optional type of the prop. |

ResponsiveOption

| Prop | Type | Description | |---|---|---| | windowSize | number | Max viewport width (px) at which params apply. | | params | Partial<Params> | Option overrides merged on top of the base params when window.innerWidth <= windowSize. |

Handler types

resetSortHandler, resetFilterHandler, and each handler in sortByOptions / filterByOptions all accept any of these three forms interchangeably:

// CSS selector string — vanilla JS
resetSortHandler: '.btn-reset-sort'

// HTMLElement — vanilla JS
resetSortHandler: document.querySelector('.btn-reset-sort')

// React ref
resetSortHandler: myResetRef  // useRef<HTMLButtonElement>(null)

Variant catalogs

Layout modes

| layout | Description | |---|---| | masonry | Classic shortest-column packing — each item drops into the currently shortest column. Items keep their natural height. (default) | | uniform | Every item snaps to a fixed cell height (rowHeight, default 220px) so the grid reads as an even tile grid. | | justified | Rows of items, each row stretched to fill the container width (Flickr / Google-Photos style). Honours rowHeight as the target row height and item data-ar (aspect-ratio) hints. |

Animation modes

The relayout transition's timing function is keyed by animation:

| animation | Timing function | Feel | |---|---|---| | ease | cubic-bezier(0.22, 0.61, 0.36, 1) | Smooth, decelerating (default). | | spring | cubic-bezier(0.34, 1.56, 0.64, 1) | Bouncy overshoot. | | linear | linear | Constant speed. | | none | linear (with 0 duration) | No animated movement; items snap into place. |

Entrance modes

Applied once, to items as they are first placed (and cleared afterwards). Disabled automatically under prefers-reduced-motion.

| entrance | Description | |---|---| | fade | Fade in from opacity 0. | | scale | Scale up into place. | | fade-scale | Fade + scale together (default). | | slide | Slide in (translate) + fade. | | none | No entrance; items appear immediately. |

Themes

theme sets data-theme on the grid, which selects the design-token palette:

| theme | Description | |---|---| | light | Light palette (default). | | dark | Dark palette. | | auto | Follows the user's prefers-color-scheme. |

Shadow

| shadow value | Result | |---|---| | false | No shadow (default). | | true | Built-in token shadow 0 6px 20px -6px rgba(15, 23, 42, 0.25) (sets --gs-flexo-shadow). | | string | Used verbatim as the box-shadow value (sets --gs-flexo-shadow). |

Layout modes, animations & theming — combined example

<GSFlexoGrid
  count={4}
  gap="14px"
  layout="justified"   // 'masonry' | 'uniform' | 'justified'
  rowHeight={220}      // target row height for justified / forced height for uniform
  animation="spring"   // 'ease' | 'spring' | 'linear' | 'none'
  duration={500}
  entrance="slide"     // 'fade' | 'scale' | 'fade-scale' | 'slide' | 'none'
  stagger={40}         // cascade the entrance, 40ms between items
  hoverLift            // lift + shadow on hover
  theme="dark"         // 'light' | 'dark' | 'auto'
  radius="12px"
  shadow               // or shadow="0 8px 24px rgba(0,0,0,.3)"
  loading={isLoading}  // shimmering skeleton while data loads
>
  {/* items */}
</GSFlexoGrid>

Identical in vanilla JS:

new GSFlexoGrid(el, {
  count: 4,
  layout: 'justified',
  rowHeight: 220,
  animation: 'spring',
  entrance: 'slide',
  stagger: 40,
  hoverLift: true,
  theme: 'dark',
  radius: '12px',
  shadow: true,
  loading: true,
});

Design tokens

The design tokens are CSS custom properties on the grid element. Override them via gsx, the radius / shadow options, or your own stylesheet:

| Token | Set by | Purpose | |---|---|---| | --gs-flexo-radius | radius | Item corner radius. | | --gs-flexo-shadow | shadow | Item resting shadow. | | --gs-flexo-shadow-hover | theme / CSS | Item shadow on hover (with hoverLift). | | --gs-flexo-lift | theme / CSS | Translate distance for the hover lift. | | --gs-flexo-bg | theme | Grid background. | | --gs-flexo-item-bg | theme | Item background. | | --gs-flexo-skeleton-base | theme | Skeleton tile base colour. | | --gs-flexo-skeleton-shine | theme | Skeleton shimmer highlight. | | --gs-flexo-skeleton-cols | count | Skeleton column count. |

Responsive

responsive entries override params below a given viewport width. Entries are auto-sorted by windowSize descending and applied when window.innerWidth <= windowSize, so smaller breakpoints win:

responsive: [
  { windowSize: 991, params: { count: 2 } },
  { windowSize: 767, params: { count: 1, gap: '6px' } },
]

The grid re-resolves its params and relayouts automatically on resize via a ResizeObserver.

License

ISC.