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

anim8-grid

v1.0.0

Published

Animated responsive image grid. Zero dependencies. Framework-agnostic.

Readme

anim8-grid

npm version npm downloads bundle size zero dependencies TypeScript license

Animated responsive image grid. Zero dependencies. Framework-agnostic. Works everywhere.

Install

npm install anim8-grid

Usage

import { createGrid } from 'anim8-grid'

const grid = createGrid(document.getElementById('grid')!, {
  images: ['/photos/01.jpg', '/photos/02.jpg', '/photos/03.jpg', '/photos/04.jpg', '/photos/05.jpg', '/photos/06.jpg'],
  gridRows: 3,
  gridColumns: 5,
  animationType: 'rotateLeft',
  animationDuration: 600,
  rotationInterval: 2000,
})

grid.stop()
grid.start()
grid.update({ animationDuration: 300, maxItemsPerCycle: 10 })
grid.destroy()

CDN / Script Tag

<script src="https://unpkg.com/anim8-grid"></script>
<script>
  const grid = anim8Grid.createGrid(document.getElementById('grid'), {
    images: ['/photos/01.jpg', '/photos/02.jpg', '/photos/03.jpg'],
  })
</script>

Options

| Option | Type | Default | Description | | --- | --- | --- | --- | | images | string[] | (required) | Image URLs. Provide more than grid cells for rotation | | gridRows | number | 4 | Number of rows | | gridColumns | number | 10 | Number of columns | | animationType | AnimationType | 'random' | Animation type (see below) | | animationDuration | number | 800 | Transition duration in ms | | animationEasing | string | 'linear' | CSS easing function | | rotationInterval | number | 3000 | Time between cycles in ms (min 300) | | itemsPerCycle | number \| 'random' | 'random' | Cells to animate per cycle | | maxItemsPerCycle | number | 3 | Max cells when itemsPerCycle is 'random' | | autoplay | boolean | true | Auto-start rotation | | hoverTrigger | boolean | false | Rotate on hover instead of timer | | frozenIndices | number[] | [] | Cell indices that never animate | | preventClick | boolean | true | Block clicks on grid items | | responsive | Record<number, Partial<GridDimensions>> | {} | Breakpoint overrides | | fullscreen | boolean | false | Fill entire viewport, auto-calculate grid | | cellSize | number | 120 | Target cell size in px (fullscreen mode) |

Methods

| Method | Description | | --- | --- | | start() | Start or resume rotation | | stop() | Pause rotation | | update(opts) | Change options at runtime without recreating the grid | | destroy() | Remove grid, cleanup all listeners |

Runtime Updates

Change animation speed, interval, items per cycle, animation type, or cell size without destroying the grid:

const grid = createGrid(container, { images, fullscreen: true, cellSize: 120 })

// These apply instantly — no rebuild
grid.update({ animationDuration: 300 })
grid.update({ rotationInterval: 800 })
grid.update({ maxItemsPerCycle: 15 })
grid.update({ animationType: 'rotateLeft' })

// This rebuilds the grid in fullscreen mode
grid.update({ cellSize: 80 })

Updatable options: animationType, animationDuration, animationEasing, rotationInterval, itemsPerCycle, maxItemsPerCycle, cellSize

Animation Types

17 built-in types:

| 2D | 3D | Combo | Special | | --- | --- | --- | --- | | fadeInOut | rotateLeft | rotateLeftScale | showHide | | slideLeft | rotateRight | rotateRightScale | random | | slideRight | rotateTop | rotateTopScale | | | slideTop | rotateBottom | rotateBottomScale | | | slideBottom | scale | | | | | rotate3d | | |

Responsive

Breakpoint-based grid dimensions using ResizeObserver:

createGrid(container, {
  images,
  gridRows: 4,
  gridColumns: 8,
  responsive: {
    1024: { gridRows: 3, gridColumns: 6 },
    768: { gridRows: 2, gridColumns: 4 },
    480: { gridRows: 1, gridColumns: 2 },
  },
})

Fullscreen Mode

Fill the entire viewport. Rows and columns auto-calculated from window.innerWidth / innerHeight and cellSize. Recalculates on resize.

createGrid(container, {
  images,
  fullscreen: true,
  cellSize: 120,
  animationType: 'random',
  rotationInterval: 1500,
  maxItemsPerCycle: 5,
})

| Resolution | cellSize: 120 | cellSize: 80 | |---|---|---| | 1920x1080 (Full HD) | 16x9 = 144 cells | 24x14 = 336 cells | | 2560x1440 (2K) | 22x12 = 264 cells | 32x18 = 576 cells | | 3840x2160 (4K) | 32x18 = 576 cells | 48x27 = 1296 cells | | 1366x768 (laptop) | 12x7 = 84 cells | 18x10 = 180 cells | | 390x844 (iPhone) | 4x8 = 32 cells | 5x11 = 55 cells | | 768x1024 (iPad) | 7x9 = 63 cells | 10x13 = 130 cells |

Container gets position: fixed; width: 100vw; height: 100vh — layer content on top with higher z-index.

Framework Examples

React

import { createGrid, type GridInstance, type UpdatableOptions } from 'anim8-grid'
import { useRef, useEffect, useCallback } from 'react'

function Anim8Grid({ images, ...options }) {
  const ref = useRef(null)

  useEffect(() => {
    const grid = createGrid(ref.current, { images, ...options })
    return () => grid.destroy()
  }, [])

  return <div ref={ref} />
}

// With runtime updates via hook
function useAnim8Grid() {
  const gridRef = useRef<GridInstance | null>(null)
  const containerRef = useRef<HTMLDivElement>(null)

  const init = useCallback((opts) => {
    gridRef.current?.destroy()
    gridRef.current = createGrid(containerRef.current!, opts)
  }, [])

  const update = useCallback((opts: UpdatableOptions) => {
    gridRef.current?.update(opts)
  }, [])

  return { containerRef, init, update }
}

Vue

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { createGrid, type GridInstance, type UpdatableOptions } from 'anim8-grid'

const props = defineProps<{ images: string[], fullscreen?: boolean, cellSize?: number }>()
const containerRef = ref<HTMLElement | null>(null)
let grid: GridInstance | null = null

function update(opts: UpdatableOptions) { grid?.update(opts) }

onMounted(() => {
  grid = createGrid(containerRef.value!, { images: props.images, fullscreen: props.fullscreen, cellSize: props.cellSize })
})
onBeforeUnmount(() => { grid?.destroy() })

defineExpose({ update })
</script>

<template><div ref="containerRef" /></template>

Angular

@Component({ selector: 'app-grid', template: '<div #container></div>', standalone: true })
export class Anim8GridComponent implements AfterViewInit, OnDestroy {
  @ViewChild('container', { static: true }) ref!: ElementRef
  @Input() images: string[] = []
  @Input() fullscreen = false
  @Input() cellSize = 120
  private grid: GridInstance | null = null

  ngAfterViewInit() {
    this.grid = createGrid(this.ref.nativeElement, {
      images: this.images, fullscreen: this.fullscreen, cellSize: this.cellSize
    })
  }

  update(opts: UpdatableOptions) { this.grid?.update(opts) }
  ngOnDestroy() { this.grid?.destroy() }
}

Astro

<div id="grid"></div>
<script>
  import { createGrid } from 'anim8-grid'
  createGrid(document.getElementById('grid')!, {
    images: Array.from({ length: 54 }, (_, i) => `/img/${i + 1}.jpg`),
    fullscreen: true,
    cellSize: 120,
  })
</script>

Full working examples: examples/

Demos

Run npx serve . from the project root and open /demo/. 8 interactive demos included:

| Demo | Description | |---|---| | Basic | Responsive grid with random animations and breakpoints | | Animations | All 15 animation types running side by side | | Hover | No auto-rotation — cells animate on mouse hover | | Fullscreen | Viewport-filling grid with live sliders for cell size, speed, interval, items | | Hero | Fullscreen grid as a page background with gradient overlay and content on top | | Frozen | Pin specific cells with frozenIndices while the rest rotate | | Speed | Slow / balanced / chaotic — same grid at 3 different speed configs | | Playground | Full control panel — tweak every parameter live with real-time code preview |

License

MIT