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

weighted-grid

v1.6.1

Published

A React grid that sizes items by relative weight and fills empty space automatically, rendered as native CSS Grid.

Readme

weighted-grid

npm version bundle size types CI license

Weight in, layout out.

Say how big each item should feel, not where it goes. The grid places items in source order, fills the container, and grows elastic items into whatever space is left. Native CSS Grid does the rendering; the JS only decides spans.

Preview

▶ Play with the live demo

Why not just CSS Grid?

| | weighted-grid | plain CSS Grid auto-flow | react-grid-layout | | ------------ | ------------------------------------------------- | ---------------------------------- | ------------------------------ | | You specify | relative importance | every span, by hand | x/y/w/h per item | | Empty space | absorbed fairly, then filled | stays empty | stays empty | | Setup | drop in children, tag a few | write the placement yourself | layout array + drag handlers | | Best for | dashboards, feeds, galleries, unknown item counts | layouts you're happy to hand-place | user-editable draggable boards | | Runtime deps | zero (react is a peer) | zero | react + drag internals |

Use plain CSS Grid when you know the layout up front. Reach for this when the item count is whatever the API returned and you still want it to look deliberate. Not a drag-and-drop library, and not trying to be — want that? Open an issue.

What's new

| Version | Highlights | | ------- | ---------- | | 1.6.0 | Core placement engine available separately; React is peer dependency | | 1.5.3 | Preset API improvements: named types and enhanced flexibility | | 1.5.0 | New preset prop with masonPreset/organicPreset, tree-shakeable subpath |

Full history in CHANGELOG.md.

Install

bun add weighted-grid   # npm / pnpm / yarn all fine

Quick start

import { Grid, GridItem } from "weighted-grid/react";

<Grid nrCols={7}>
  <GridItem weight={4}>hero</GridItem>
  <GridItem>a</GridItem>
  <GridItem>b</GridItem>
  <GridItem>c</GridItem>
</Grid>;

weight works like flexbox flex: how much of the grid does this item get. Equal weights give equal squares.

The mental model

Three ideas, and you've seen the whole library.

1 · Weight sizes both axes. weight={2} is a 2×2 block. Pin one axis with cols or rows and weight keeps driving the other:

<GridItem weight={4}>elastic on both axes</GridItem>
<GridItem cols={3} weight={2}>3 columns wide, weight sets the height</GridItem>
<GridItem cols={2} rows={2}>strict — never stretches</GridItem>

Elasticity is per axis. Only pinning both makes an item fully rigid.

2 · Leftover space gets absorbed, fairly. Elastic items stretch into the gaps around them, split evenly between the neighbours flanking a gap — never all growth dumped on one side. Cap it globally with stretch={n} on the Grid, or per item.

3 · Whatever's left is yours. Cells nothing could reach are merged into rectangular blocks and handed to fillComponent — one node per block, not one per cell. Skip the prop and they stay empty.

<Grid
  nrCols={12}
  stretch={2}
  fillComponent={(rect) => <Placeholder {...rect} />}
>
  {items.map((item) => (
    <GridItem key={item.id}>{item.label}</GridItem>
  ))}
</Grid>

API

<Grid>

| Prop | Type | Default | What it does | | ----------------- | ---------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------ | | nrCols | number | 7 | Column count. Tracks scale with the container width. | | nrRows | number | — | Minimum row tracks — a floor, not a cap. Omit it; the grid counts what its items need. | | gap | number \| string | 8 | Gutter between items (px if a number). | | rowHeight | "auto" \| number \| string | "auto" | "auto" splits the parent's height into bands (parent needs a height). A value = fixed rows, grid grows downward. | | stretch | number | Infinity | How many extra cells an elastic axis may grow to absorb gaps. 0 turns it off. | | fillComponent | ReactNode \| (rect) => ReactNode | — | Rendered into the merged leftover blocks. Gets { row, col, rowSpan, colSpan } when it's a function. | | preset | PresetFn | — | Auto-assigns props per item — see Presets. Explicit GridItem props always win. | | showGrid | boolean | false | Debug overlay drawn exactly on the real gutter, so guides can't drift from the actual layout. | | animateSize | boolean | false | FLIP-transition an item's size when its span changes. | | animatePosition | boolean | false | Same, for position. Best for grids where items nudge rather than jump. | | className | string | — | On the outer container. | | style | CSSProperties | — | Merged into the outer container's inline style. |

<GridItem>

| Prop | Type | Default | What it does | | ----------------------- | -------- | ------- | --------------------------------------------------------------------------------- | | weight | number | 1 | Relative size. Fills whichever axis isn't pinned; pin neither and it drives both. | | cols | number | — | Exact column span. Pins the horizontal axis. Clamped to nrCols. | | rows | number | — | Exact row span. Pins the vertical axis. | | stretch | number | — | Overrides the grid's stretch for this item — including on a pinned axis. | | stretchX / stretchY | number | — | Same, per axis. |

💡 stretch on an item cuts both ways: let a cols-pinned card grow into a gap anyway, or stop one greedy elastic item from eating the whole row.

Presets

A preset is just a function — ({ count, nrCols, nrRows }) => Partial<GridItemProps>[] — that hands each item its defaults. Perfect for "here are 40 cards, make it look intentional".

import { masonPreset } from "weighted-grid/presets";

<Grid nrCols={8} preset={masonPreset()}>
  {items.map((item) => (
    <GridItem key={item.id}>{item.label}</GridItem>
  ))}
</Grid>;
  • masonPreset(brick = 2) — running-bond brick rows, every other row offset by half a brick.
  • organicPreset(seed = 1) — a drifting mosaic of small/medium/large tiles, scaled to nrCols.

They live on the weighted-grid/presets subpath, so a preset you don't import (and its code) never reaches your bundle. Rolling your own is a ten-liner:

import type { PresetFn } from "weighted-grid/presets";

const stripes: PresetFn = ({ count, nrCols }) =>
  Array.from({ length: count }, (_, i) => ({
    weight: i % nrCols < nrCols / 2 ? 2 : 1,
  }));

Keep the function reference stable (module scope, or useCallback) — Grid memoizes on it.

Responsive columns

There's no wrap prop, because there doesn't need to be one. Spans clamp to nrCols, so dropping the column count at a breakpoint reflows everything into fewer columns and more rows, flex-wrap style:

<Grid nrCols={isMobile ? 2 : 6}>...</Grid>

Examples

Every one of these runs in the live demo — the source is a few lines long and worth a skim.

| Example | Shows off | | ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | | organic-raw | organicPreset, flat color, cropped into a strip that scrolls sideways | | organic-styled | Same preset, real cards, stretch + fillComponent doing the work | | responsive-cols | nrCols collapsing on narrow viewports | | prop-matrix | Every sizing prop side by side — the cheat sheet | | pinned-spans | Strict cols/rows items with elastic ones flowing around them | | row-height | "auto" bands vs. fixed rows, with live controls | | modes | masonPreset and organicPreset on the same content |

Development

bun install
bun run test        # bun test
bun run typecheck
bun run build       # vite → dist/
bun run format      # biome check --write
bun run demo:dev    # the demo app in demo/

License

MIT © jayF0x