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

spectime

v0.1.1

Published

Spectime timeline components built on chronon-timeline.

Readme

spectime

spectime is an opinionated UI layer on top of my library chronon-timeline.

It doesn’t reimplement the timeline engine — you still use chronon-timeline for the core primitives (Timeline, Row, Subrow, Item), the data model (Range, RowDefinition, ItemDefinition), and state helpers like useTimeline() (re-exported as useSpectimeTimeline() from spectime). spectime adds styling, convenience wrappers, and a small hook for turning a flat list of items into subrows (lanes).

Composition pattern (how you build UIs with it)

chronon-timeline is built around the composition pattern: instead of a single “mega timeline component”, you compose a timeline from small primitives:

  • Timeline (shell + context)
  • TimeAxis (markers/labels)
  • Row (one row in the timeline, with optional sidebar)
  • Subrow (a lane inside a row)
  • Item (a span in time)

spectime keeps that same model: you compose your own layout, but with pre-styled wrappers and a helper hook for lane packing.

Mentally, it looks like:

SpectimeTimelineProvider (context + interactions)
  SpectimeTimelineContainer (shell)
    SpectimeTimeAxis (headers)
    SpectimeRow[] (rows)
      SpectimeSubrow[] (lanes)
        SpectimeItem[] (items)

Installation

spectime depends on chronon-timeline and expects these peer deps to be installed in your app:

npm i spectime chronon-timeline react react-dom react-virtuoso

Building blocks (what each exported piece does)

Core wrappers:

  • SpectimeTimelineProvider: wraps TimelineContextProvider and enables mouse pan/zoom + “autopan until user interaction”; exposes useSpectimeTimelineContext() for resetAutopan().
  • SpectimeTimelineContainer: wraps Timeline and applies the Spectime shell styling (and default RTL direction unless you override).
  • SpectimeTimeAxis: renders the main time axis (plus the smaller hour axis + optional highlight row) using Spectime’s marker definitions.
  • SpectimeRow: wraps chronon-timeline’s VirtualizedRow and turns on virtualizeSubrows (subrow virtualization) for better performance with many lanes.
  • SpectimeSubrow: a styled wrapper around Subrow.
  • SpectimeItem: a styled wrapper around Item with an inner container for your custom content.

Common add-ons:

  • SpectimeSidebar: a styled sidebar renderer you can pass as sidebar to a row.
  • SpectimeTimeCursor / SpectimeCurrentTimeCursor: cursor components for rendering “now” or an arbitrary timestamp.

Helpers/hooks:

  • useSpectimeTimeline: re-export of chronon-timeline’s useTimeline() hook (range + zoom state).
  • useVisibleTimelineItems: groups items into non-overlapping lanes (subrows) for the current visible range (must be used inside SpectimeTimelineProvider).
  • currentTimeAtom: a Jotai atom that ticks Date.now() every second (used by SpectimeCurrentTimeCursor and autopan).

Basic usage

import { useState } from 'react';
import type { ItemDefinition, Range, RowDefinition } from 'chronon-timeline';

import {
  SpectimeItem,
  SpectimeRow,
  SpectimeSubrow,
  SpectimeTimeAxis,
  SpectimeTimelineContainer,
  SpectimeTimelineProvider,
  currentTimeAtom,
  useSpectimeTimeline,
  useVisibleTimelineItems,
} from 'spectime';

export const MyTimeline = ({
  rows,
  items,
}: {
  rows: RowDefinition[];
  items: ItemDefinition[];
}) => {
  const [range, setRange] = useState<Range>(() => {
    const now = Date.now();
    return { start: now - 60 * 60 * 1000, end: now + 60 * 60 * 1000 };
  });

  const timeline = useSpectimeTimeline({ range, setRange });

  return (
    <SpectimeTimelineProvider {...timeline} currentTimeAtom={currentTimeAtom}>
      <TimelineContents rows={rows} items={items} />
    </SpectimeTimelineProvider>
  );
};

const TimelineContents = ({
  rows,
  items,
}: {
  rows: RowDefinition[];
  items: ItemDefinition[];
}) => {
  const SUBROW_HEIGHT_PX = 60;

  const { subrowsByRow, rowIdWithMostVisibleLanes, isWeekly } = useVisibleTimelineItems({
    rows,
    items,
  });

  return (
    <SpectimeTimelineContainer>
      <SpectimeTimeAxis isWeekly={isWeekly} />

      {rows.map((row) => (
        <SpectimeRow
          key={row.id}
          {...row}
          subrowHeight={SUBROW_HEIGHT_PX}
          ignoreRefs={row.id !== rowIdWithMostVisibleLanes}
        >
          {(subrowsByRow[row.id] ?? []).map((lane, laneIndex) => (
            <SpectimeSubrow key={`${row.id}-${laneIndex}`}>
              {lane.map((item) => (
                <SpectimeItem key={item.id} id={item.id} span={item.span}>
                  {String(item.id)}
                </SpectimeItem>
              ))}
            </SpectimeSubrow>
          ))}
        </SpectimeRow>
      ))}
    </SpectimeTimelineContainer>
  );
};

Notes:

  • SpectimeRow wraps chronon-timeline’s VirtualizedRow and enables virtualizeSubrows by default.
  • This package ships styles bundled into the JS build (no separate CSS import).

Subrows (lanes) and useVisibleTimelineItems

In chronon-timeline, each Row can contain multiple Subrows. A subrow is effectively a “lane” used to display items that would otherwise overlap in time.

useVisibleTimelineItems({ rows, items }) is a convenience hook that:

  • Must be called from a component rendered inside SpectimeTimelineProvider (it reads the current range from timeline context).
  • Filters items to what intersects the current timeline range.
  • Groups items by rowId and packs them into the minimum number of non-overlapping lanes (subrows) so items in the same lane never overlap in time.
  • Exposes isWeekly, a boolean derived from the current visible range size, which is typically used to switch time-axis marker density and to optionally expand item spans to full-day blocks when zoomed out.

Return shape:

  • subrowsByRow: Record<string, ItemDefinition[][]> (each inner array is one lane/subrow)
  • rowIdWithMostVisibleLanes: a helper for the ignoreRefs optimization pattern
  • isWeekly: whether the visible range is “zoomed out” (currently ~3+ days)

Why a fixed subrowHeight helps performance

subrowHeight is intentionally a required input, because it removes a whole class of expensive layout work:

  • No DOM measuring for lane sizing. When every subrow has a known fixed height, the row layout can be expressed via a single CSS variable (--tl-subrow-height) instead of repeatedly measuring content.
  • Virtualization becomes predictable. SpectimeRow uses VirtualizedRow + react-virtuoso for subrow virtualization. A stable subrowHeight lets the virtualizer compute scroll offsets and overscan purely from math (defaultItemHeight), instead of doing extra work to guess/measure heights.
  • Less churn when lots of subrows exist. If you have many lanes, keeping their height fixed prevents “useless recalculations” (reflow/relayout passes triggered by variable heights) and makes scrolling/panning smoother.

Practical tip: treat subrowHeight as a constant per zoom mode (e.g. 60 for dense view, 40 for weekly), and avoid dynamically changing it per lane.

Provider helpers

SpectimeTimelineProvider is a thin wrapper around chronon-timeline’s TimelineContextProvider that also enables:

  • mouse pan + zoom
  • auto-pan that keeps “now” moving until the user interacts

If you need access to timeline context plus resetAutopan(), use useSpectimeTimelineContext(). This is useful when you jump the range programmatically (e.g. a “preset range” toolbar) and want auto-pan to resume.

Local demo (this repo)

This repo contains dev-only examples under src/examples/* (not shipped in the npm package):

npm install
npm run dev