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 🙏

© 2025 – Pkg Stats / Ryan Hefner

modern-react-timeline-component

v0.5.1

Published

A modern, interactive timeline component for React with drag-and-drop, zoom, and customizable rendering

Downloads

309

Readme

Modern React Timeline Component

A modern, feature-rich timeline/calendar component for React applications. Built with TypeScript, Tailwind CSS, and shadcn/ui components.

Features

Interactive & Modern

  • 🎯 Drag and drop items between groups
  • 🔍 Zoom in/out functionality
  • 📅 Sticky headers with smooth scrolling
  • 🎨 Customizable item and group rendering
  • 🌍 Multi-language support (11+ locales)

Powerful

  • 📊 Category-based styling
  • 🎭 Custom icons with tooltips
  • 📐 Resize items by dragging edges
  • 📏 Variable row height (compact to spacious)
  • 🔄 Automatic item stacking for overlapping items
  • ⌨️ Keyboard accessible
  • 🎬 Smooth animations

🛠️ Developer Friendly

  • 💪 Full TypeScript support
  • 🎨 Built with Tailwind CSS
  • 📦 Tree-shakeable
  • 🔧 Highly customizable
  • 📖 Comprehensive type definitions

Installation

npm install modern-react-timeline-component
# or
yarn add modern-react-timeline-component
# or
pnpm add modern-react-timeline-component

Peer Dependencies

npm install react react-dom dayjs lucide-react

✨ Zero-Config Setup

That's it! The component works out of the box with zero configuration:

  • CSS is automatically injected - No manual imports needed
  • Works with any bundler - Webpack, Vite, Next.js, etc.
  • No build configuration required - Just import and use
import { Timeline } from 'modern-react-timeline-component';
// Styles are automatically included - nothing else needed!

Optional: Tailwind CSS Integration

If you're using Tailwind CSS and want to customize the component's styles, you can optionally use our preset:

// tailwind.config.js
module.exports = {
  presets: [
    require('modern-react-timeline-component/tailwind-preset')
  ],
  content: [
    './src/**/*.{js,jsx,ts,tsx}',
    // your other content paths...
  ],
  // ... rest of your config
}

Note: This is completely optional. The component works perfectly without any Tailwind configuration.

Quick Start

import { Timeline, TimelineItem, TimelineGroupData, Category } from 'modern-react-timeline-component';

const groups: TimelineGroupData[] = [
  { id: 'group-1', title: 'Team Alpha' },
  { id: 'group-2', title: 'Team Beta' },
];

const categories: Category[] = [
  {
    id: 'dev',
    title: 'Development',
    background_color: '#3b82f6',
    text_color: '#ffffff'
  },
];

const items: TimelineItem[] = [
  {
    id: '1',
    group: 'group-1',
    title: 'Project Kickoff',
    start: new Date('2024-01-15T09:00:00'),
    end: new Date('2024-01-15T17:00:00'),
    category: 'dev',
  },
];

function App() {
  return (
    <div className="h-screen p-4">
      <Timeline
        groups={groups}
        categories={categories}
        items={items}
        locale="en"
        editable={true}
        selectable={true}
        showLegend={true}
        showControls={true}
      />
    </div>
  );
}

Props

Timeline Component

| Prop | Type | Default | Description | |------|------|---------|-------------| | groups | TimelineGroupData[] | required | Array of group/row definitions | | categories | Category[] | required | Array of category definitions for styling | | items | TimelineItem[] | required | Array of timeline items to display | | locale | string | 'en' | Locale code for date/time formatting | | defaultTimeStart | Date | 6 months ago | Start of the total scrollable range | | defaultTimeEnd | Date | 6 months ahead | End of the total scrollable range | | visibleTimeStart | Date | 3 days ago | Defines the start of the visible range at 100% zoom | | visibleTimeEnd | Date | 4 days ahead | Defines the end of the visible range at 100% zoom | | stickyHeader | boolean | true | Enable sticky date headers | | editable | boolean | true | Allow drag and resize | | selectable | boolean | true | Allow item selection | | showLegend | boolean | true | Show category legend | | showControls | boolean | true | Show zoom/navigation controls | | showZoomLevel | boolean | true | Show zoom level percentage in controls | | groupBarWidth | number | 192 | Width of the group labels column (px) | | rowHeight | number | 60 | Height of each timeline row/group (px) | | selectedItemId | string \| null | null | ID of currently selected item | | groupRenderer | function | - | Custom group label renderer | | groupHeaderRenderer | function | - | Custom group header renderer | | itemRenderer | function | - | Custom item renderer | | controlsRenderer | function | - | Custom controls renderer | | onItemClick | function | - | Called when item is clicked | | onItemSelect | function | - | Called when item selection changes | | onItemMove | function | - | Called when item is dragged | | onItemResize | function | - | Called when item is resized |

Understanding Time Ranges

The timeline uses two different time range concepts:

  • Total Range (defaultTimeStart / defaultTimeEnd): Defines the entire scrollable area. Users can scroll through this entire range. Defaults to 6 months before and after today.

  • Visible Range (visibleTimeStart / visibleTimeEnd): Defines what time period is shown at 100% zoom level. This controls the "base" zoom - at zoom level 1.0, exactly this time range fits in the viewport. Defaults to 1 week (3 days before, 4 days after today).

Example:

<Timeline
  // Total scrollable range: 1 year
  defaultTimeStart={dayjs().subtract(6, 'month').toDate()}
  defaultTimeEnd={dayjs().add(6, 'month').toDate()}

  // At 100% zoom, show 5 weeks
  visibleTimeStart={dayjs().startOf('week').toDate()}
  visibleTimeEnd={dayjs().add(4, 'week').endOf('week').toDate()}

  // ... other props
/>

In this example:

  • Users can scroll through a full year of data
  • At 100% zoom, they see 5 weeks at a time
  • Zooming in (200%) shows ~2.5 weeks
  • Zooming out (50%) shows ~10 weeks

Type Definitions

TimelineGroupData

interface TimelineGroupData {
  id: string;
  title: string;
  height?: number;
  [key: string]: any; // Additional custom properties
}

TimelineItem

interface TimelineItem {
  id: string;
  group: string;  // References group id
  title: string;
  start: Date;
  end: Date;
  show_duration?: boolean;
  icon?: {
    name: string;           // Icon name from lucide-react
    text: string;           // Tooltip text
    position: "left" | "right";
  };
  category?: string;        // References category id
  style?: React.CSSProperties;
}

Category

interface Category {
  id: string;
  title: string;
  background_color: string;
  text_color: string;
}

Advanced Usage

Custom Item Renderer

import { ItemRendererProps } from 'modern-react-timeline-component';

const customItemRenderer = ({ item, itemContext, getItemProps, getResizeProps }: ItemRendererProps) => {
  const { left: leftResize, right: rightResize } = getResizeProps();
  const itemProps = getItemProps();

  return (
    <div {...itemProps}>
      {itemContext.useResizeHandle && <div {...leftResize} />}
      
      <div className="p-2">
        <h4>{itemContext.title}</h4>
        <p>{item.category}</p>
      </div>
      
      {itemContext.useResizeHandle && <div {...rightResize} />}
    </div>
  );
};

<Timeline
  itemRenderer={customItemRenderer}
  // ... other props
/>

Custom Group Renderer

const customGroupRenderer = ({ group }) => (
  <div className="flex items-center gap-2">
    <Avatar src={group.avatar} />
    <div>
      <div className="font-bold">{group.title}</div>
      <div className="text-sm text-gray-500">{group.description}</div>
    </div>
  </div>
);

<Timeline
  groupRenderer={customGroupRenderer}
  // ... other props
/>

Custom Controls Renderer

import { ControlsRendererProps } from 'modern-react-timeline-component';

const customControlsRenderer = ({ controls, state }: ControlsRendererProps) => (
  <div className="flex gap-2 p-4">
    <button onClick={controls.zoomIn} disabled={!state.canZoomIn}>
      Zoom In
    </button>
    <button onClick={controls.zoomOut} disabled={!state.canZoomOut}>
      Zoom Out
    </button>
    <button onClick={controls.goHome}>
      Reset
    </button>
    <span>Zoom: {Math.round(state.zoom * 100)}%</span>
  </div>
);

<Timeline
  controlsRenderer={customControlsRenderer}
  // ... other props
/>

Scroll to Group Programmatically

import { useRef } from 'react';
import { Timeline, TimelineRef } from 'modern-react-timeline-component';

function App() {
  const timelineRef = useRef<TimelineRef>(null);

  const scrollToGroup = (groupId: string) => {
    timelineRef.current?.scrollToGroup(groupId);
  };

  return (
    <>
      <button onClick={() => scrollToGroup('group-1')}>
        Go to Team Alpha
      </button>
      <Timeline
        ref={timelineRef}
        // ... other props
      />
    </>
  );
}

Custom Group Header Renderer

Customize the header area above the group labels (top-left corner of the timeline):

import { Users } from 'lucide-react';

const customGroupHeaderRenderer = ({ width, height }) => {
  return (
    <div
      className="bg-gradient-to-br from-primary/10 to-primary/5 border-r flex items-center justify-center"
      style={{ width: `${width}px`, height: `${height}px` }}
    >
      <div className="flex flex-col items-center gap-1">
        <div className="flex items-center gap-2">
          <Users className="w-4 h-4 text-primary" />
          <span className="font-semibold text-sm">Teams</span>
        </div>
        <span className="text-xs text-muted-foreground">
          {groups.length} teams
        </span>
      </div>
    </div>
  );
};

<Timeline
  groupHeaderRenderer={customGroupHeaderRenderer}
  // ... other props
/>

Use cases:

  • Display a logo or branding
  • Show summary information (e.g., total groups count)
  • Add custom styling to match your design system
  • Create a title for the groups section

Event Handlers

<Timeline
  onItemClick={(item) => {
    console.log('Item clicked:', item);
  }}
  onItemMove={(itemId, newTime, newGroupId) => {
    console.log('Item moved:', itemId, newTime, newGroupId);
    // Update your state here
  }}
  onItemResize={(itemId, newStart, newEnd) => {
    console.log('Item resized:', itemId, newStart, newEnd);
    // Update your state here
  }}
  // ... other props
/>

Supported Locales

The component supports 11+ locales with proper date/time formatting:

  • 🇬🇧 English (en)
  • 🇪🇸 Spanish (es)
  • 🇫🇷 French (fr)
  • 🇩🇪 German (de)
  • 🇳🇱 Dutch (nl)
  • 🇯🇵 Japanese (ja)
  • 🇨🇳 Chinese (zh)
  • 🇵🇹 Portuguese (pt)
  • 🇷🇺 Russian (ru)
  • 🇸🇦 Arabic (ar)
  • 🇮🇳 Hindi (hi)

Styling

The component uses Tailwind CSS classes. You can customize the theme by extending your Tailwind configuration:

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        // Override timeline colors
        primary: {
          DEFAULT: '#your-color',
          // ... 
        },
      },
    },
  },
}

License

MIT © Jeremie Van de Walle

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

If you encounter any issues or have questions, please file an issue on the GitHub repository.