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

rc-sheet-viewer-17

v2.1.49

Published

A high-performance React component for viewing and editing Excel and CSV files with a Google Sheets-like interface. Handles 100k+ rows with virtualized rendering, embedded chart extraction, pivot table reconstruction, and a clean, familiar spreadsheet UI.

Readme

rc-sheet-viewer-17

A high-performance React component for viewing and editing Excel and CSV files with a Google Sheets-like interface. Handles 100k+ rows with virtualized rendering, embedded chart extraction, pivot table reconstruction, and a clean, familiar spreadsheet UI. Compatible with React 17, 18, and 19.

TypeScript React 17+ Storybook


Features

  • File format support.xlsx, .xls, and .csv parsed entirely client-side
  • Virtualized grid — row and column virtualization via TanStack Virtual; renders 100k+ rows smoothly
  • Google Sheets-like UI — column headers (A, B, ... Z, AA ...), row numbers, sticky headers, sheet tabs, formula bar, status bar
  • View & edit modes — read-only viewing or inline cell editing, controlled via props
  • Multiple sheets — tab-based sheet switching with per-sheet selection state
  • Range highlighting — pass Excel-style references like A1:D10 to highlight and scroll into view
  • Clear highlight API — remove the active range via ref.current?.clearHighlight() or setHighlight('')
  • Single-cell focus without range tint — clicking one cell focuses it and updates the formula bar, but does not create a visible 1x1 highlight
  • Custom highlight colors — override the selection fill and border color via props
  • Separate search highlight — Ctrl+F search uses its own independent highlight with customizable colors
  • Grid lines — data-driven visible borders on arbitrary ranges (like Excel's "All Borders")
  • Theme system — override the entire library color palette via a single theme prop object
  • Show/hide toolbar — control full toolbar visibility with showToolbar, or hide only the filename with showFileName
  • Embedded charts — extracts charts from Excel files and renders them as floating overlays using Chart.js
  • Pivot table reconstruction — reads pivot table definitions from XLSX internals and reconstructs the output
  • Search — Ctrl+F search across the active sheet with dedicated highlight colors
  • Download — export the current data as .xlsx or .csv
  • Charts from selection — select a data range and create bar, line, pie, or area charts dynamically
  • Copy & paste with formatting — Ctrl+C/V with both HTML (preserving styles) and TSV clipboard formats
  • Undo/redo — Ctrl+Z and Ctrl+Y (or Ctrl+Shift+Z) for cell edits and paste
  • Column/row resize — drag column and row header edges to resize
  • Merged cells — visually renders merged cell ranges from Excel files
  • Text wrapping — cells with wrapText style render multi-line content
  • Cell comments — red triangle indicator with hover tooltip for commented cells
  • Conditional formatting — rule-based cell styling (value comparisons, color scales, data bars)
  • Data validation — input validation with dropdown lists, number/date ranges, and error UI
  • Formula engine — parse and evaluate Excel-style formulas (24 built-in functions: SUM, IF, VLOOKUP, etc.)
  • Imperative API — access sheet data, navigate sheets, resize columns, manage comments, undo/redo, and more via ref
  • Selection callback with DOM infoonSelectionChange provides the anchor cell's HTMLElement and DOMRect
  • Highlightable — disable all cell/range highlight visuals with highlightable={false}
  • Sheet select callbackonSheetSelect fires when the user clicks a sheet tab
  • Large cell content — active cell expands to show full text (Google Sheets-style)
  • Instance isolation — multiple <SheetViewer /> components on the same page are fully independent
  • Nested container support — horizontal trackpad/mouse scrolling works in nested scrollable containers
  • Zero global styles — all CSS scoped under .sheet-viewer with sv- prefixed classes

Installation

pnpm add rc-sheet-viewer-17
# or
npm install rc-sheet-viewer-17
# or
yarn add rc-sheet-viewer-17

Peer dependencies: react >= 17 and react-dom >= 17


Quick Start

import { SheetViewer } from 'rc-sheet-viewer-17';
import 'rc-sheet-viewer-17/style.css';

function App() {
  return (
    <SheetViewer
      source="/path/to/spreadsheet.xlsx"
      mode="view"
      height="100vh"
      width="100%"
      downloadable
      searchable
    />
  );
}

Source Types

The source prop accepts multiple formats:

// URL string — fetched automatically
<SheetViewer source="https://example.com/data.xlsx" />

// File object — from an <input type="file"> or drag-and-drop
<SheetViewer source={fileFromInput} />

// ArrayBuffer — from any binary source
<SheetViewer source={arrayBuffer} />

// TypedArray (Uint8Array, etc.)
<SheetViewer source={uint8Array} />

Props

| Prop | Type | Default | Description | |---|---|---|---| | source | string \| File \| ArrayBuffer \| ArrayBufferView | — | Data source to load | | mode | 'view' \| 'edit' | 'view' | View-only or editable mode | | activeSheet | string | — | Controlled active sheet name | | highlight | string | — | Excel-style range to highlight (e.g. "A1:D10") | | highlightColor | string | — | Custom fill color for the highlighted range (e.g. "rgba(255,0,0,0.1)") | | highlightBorderColor | string | — | Custom border color for the highlighted range (e.g. "#ff0000") | | highlightable | boolean | true | When false, all selection/highlight visuals are suppressed | | gridLines | GridLineConfig[] | — | Draw visible cell borders on specific ranges (like Excel's All Borders) | | showToolbar | boolean | true | Show or hide the entire toolbar row | | showFileName | boolean | true | Show or hide only the filename/logo in the toolbar. Charts & Download remain visible | | theme | SheetViewerTheme | — | Override the entire color palette of the library | | searchMatchColor | string | — | Background color for search match cells (default: rgba(255,213,79,0.35)) | | searchActiveColor | string | — | Background color for the active/current search match (default: rgba(255,152,0,0.55)) | | onSheetChange | (sheetName: string) => void | — | Called when the user switches sheets | | onSheetSelect | (sheetName: string) => void | — | Called when the user clicks a sheet tab | | onCellChange | (sheet, row, col, value) => void | — | Called when a cell is edited | | onSelectionChange | (ranges: CellRange[], cellInfo?) => void | — | Called when selection changes; includes anchor cell element + bounding rect | | downloadable | boolean | false | Show a download button in the toolbar | | chartable | boolean | true | Show Charts button in toolbar | | searchable | boolean | true | Enable Ctrl+F search | | height | number \| string | '100%' | Container height (px or CSS value) | | width | number \| string | '100%' | Container width (px or CSS value) | | className | string | '' | Additional CSS class on the root element |


Imperative API (Ref)

Access sheet data and control navigation programmatically:

import { useRef } from 'react';
import { SheetViewer } from 'rc-sheet-viewer-17';
import type { SheetViewerHandle } from 'rc-sheet-viewer-17';

function App() {
  const ref = useRef<SheetViewerHandle>(null);

  const handleClick = () => {
    // Get all sheet names
    console.log(ref.current?.getSheetNames());

    // Get data for the active sheet
    const data = ref.current?.getSheetData();
    console.log(`${data?.rows} rows, ${data?.cols} cols`);

    // Get data for a specific cell range
    const rangeData = ref.current?.getCellRangeData('A1:C10');
    console.log(rangeData); // CellValue[][] | null

    // Get data for the current mouse/drag selection
    const selData = ref.current?.getSelectedRangeData();

    // Switch to a specific sheet
    ref.current?.setActiveSheet('Sheet2');

    // Highlight a range (triggers onSelectionChange)
    ref.current?.setHighlight('A1:F20');

    // Highlight silently (does NOT trigger onSelectionChange)
    ref.current?.setHighlight('A1:F20', { silent: true });

    // Clear the current highlight / selection
    ref.current?.clearHighlight();

    // Clear silently (does NOT trigger onSelectionChange)
    ref.current?.clearHighlight({ silent: true });

    // Get the current highlight/selection range string
    const range = ref.current?.getHighlight(); // e.g. "A1:D10" or null

    // Undo / redo
    if (ref.current?.canUndo()) ref.current.undo();
    if (ref.current?.canRedo()) ref.current.redo();
  };

  return (
    <>
      <button onClick={handleClick}>Inspect</button>
      <SheetViewer ref={ref} source={file} />
    </>
  );
}

Handle Methods

| Method | Return | Description | |---|---|---| | getSheetNames() | string[] | All sheet names in the workbook | | getSheetData(name?) | SheetData \| null | Data for a sheet (defaults to active) | | getActiveSheet() | string \| null | Currently active sheet name | | getAllSheets() | Record<string, SheetData> | All parsed sheets | | getFileName() | string \| null | The loaded file name | | getCellRangeData(range, name?) | CellValue[][] \| null | Get data for an Excel-style range | | getSelectedRangeData() | CellValue[][] \| null | Get data for the current drag selection | | setActiveSheet(name) | void | Switch to a sheet | | setHighlight(range, options?) | void | Highlight a range. Pass '' to clear. { silent: true } suppresses onSelectionChange | | clearHighlight(options?) | void | Remove the current highlight. { silent: true } suppresses onSelectionChange | | getHighlight() | string \| null | Current highlight/selection range string (e.g. "A1:D10") | | setColumnWidth(col, width, name?) | void | Set column width in pixels (min 30px) | | setRowHeight(row, height, name?) | void | Set row height in pixels (min 20px) | | getCellComment(cellRef, name?) | CellComment \| null | Get comment for a cell | | setCellComment(cellRef, text, author?, name?) | void | Set or remove a cell comment | | undo() | void | Undo the last cell edit or paste | | redo() | void | Redo the last undone edit | | canUndo() | boolean | Whether undo is available | | canRedo() | boolean | Whether redo is available |


Custom Highlight Colors

Override the default blue selection with any fill and border color:

<SheetViewer
  source={file}
  highlight="B2:D10"
  highlightColor="rgba(255, 0, 0, 0.12)"
  highlightBorderColor="#e53935"
/>

To remove a highlight programmatically:

ref.current?.clearHighlight();
ref.current?.clearHighlight({ silent: true });

// Equivalent clear via the existing API:
ref.current?.setHighlight('');

Grid Lines

Draw visible Excel-style cell borders on arbitrary ranges:

import type { GridLineConfig } from 'rc-sheet-viewer-17';

const gridLines: GridLineConfig[] = [
  // Bold blue header row with background
  { range: 'A1:F1', borderColor: '#1a73e8', borderWidth: 2, bgColor: '#e8f0fe' },
  // Light gray border for all data cells
  { range: 'A2:F50', borderColor: '#dadce0', borderWidth: 1 },
  // Dashed red highlight zone
  { range: 'C2:C50', borderColor: '#ea4335', borderWidth: 1, borderStyle: 'dashed' },
];

<SheetViewer source={file} gridLines={gridLines} />

GridLineConfig fields

| Field | Type | Default | Description | |---|---|---|---| | range | string | required | Excel-style range e.g. "A1:D10" | | borderColor | string | var(--sv-color-border) | Border color | | borderWidth | number | 1 | Border width in pixels | | borderStyle | 'solid' \| 'dashed' \| 'dotted' | 'solid' | Border style | | bgColor | string | — | Optional background fill for cells in this range |


Theme Customization

Override the entire library color palette with a theme prop:

import type { SheetViewerTheme } from 'rc-sheet-viewer-17';

const darkTheme: SheetViewerTheme = {
  bgColor: '#1e1e2e',
  surfaceColor: '#181825',
  borderColor: '#313244',
  borderLightColor: '#45475a',
  textColor: '#cdd6f4',
  textSecondaryColor: '#a6adc8',
  textMutedColor: '#6c7086',
  primaryColor: '#89b4fa',
  primaryLightColor: '#313244',
  primaryBgColor: '#1e1e2e',
  selectionColor: 'rgba(137,180,250,0.15)',
  selectionHoverColor: 'rgba(137,180,250,0.25)',
  headerBgColor: '#181825',
  headerTextColor: '#a6adc8',
  hoverColor: '#313244',
};

<SheetViewer source={file} theme={darkTheme} />

SheetViewerTheme fields

| Field | CSS Variable | Description | |---|---|---| | bgColor | --sv-color-bg | Main background color | | surfaceColor | --sv-color-surface | Secondary background (toolbars, headers) | | borderColor | --sv-color-border | Default border color | | borderLightColor | --sv-color-border-light | Light border color | | textColor | --sv-color-text | Primary text color | | textSecondaryColor | --sv-color-text-secondary | Secondary text color | | textMutedColor | --sv-color-text-muted | Muted/placeholder text | | primaryColor | --sv-color-primary | Primary accent color | | primaryLightColor | --sv-color-primary-light | Light primary variant | | primaryBgColor | --sv-color-primary-bg | Very light primary background | | selectionColor | --sv-color-selection | Range selection fill color | | selectionHoverColor | --sv-color-selection-hover | Range selection fill on hover | | headerBgColor | --sv-color-header-bg | Column/row header background | | headerTextColor | --sv-color-header-text | Column/row header text | | hoverColor | --sv-color-hover | Cell hover background |

You can also override these directly in CSS:

.sheet-viewer {
  --sv-color-primary: #7c3aed;
  --sv-color-selection: rgba(124, 58, 237, 0.1);
}

Search Highlight Colors

Customize the colors used to highlight search matches independently from the selection:

<SheetViewer
  source={file}
  searchMatchColor="rgba(255, 213, 79, 0.45)"     // all matches — yellow
  searchActiveColor="rgba(255, 152, 0, 0.7)"      // current match — orange
/>

Press Ctrl+F (Cmd+F) to open search. Matches are shown with a dedicated highlight that does not interfere with the user's cell selection.

Priority: Search match highlights always take precedence over the range selection highlight — if a cell is both selected and a search match, the search color wins.


Show / Hide Toolbar

// Hide the entire toolbar row
<SheetViewer source={file} showToolbar={false} />

// Hide just the filename/logo, keep Charts and Download buttons
<SheetViewer source={file} showFileName={false} downloadable chartable />

// Show everything (defaults)
<SheetViewer source={file} showToolbar={true} showFileName={true} downloadable chartable />

showToolbar={false} removes the whole toolbar row. showFileName={false} keeps the toolbar but hides only the logo and filename text, leaving Charts and Download visible.


Selection Callback with Cell DOM Info

onSelectionChange provides the DOM element and bounding rect of the anchor cell for drag selections and programmatic highlights, useful for positioning custom popovers or tooltips:

<SheetViewer
  source={file}
  onSelectionChange={(ranges, cellInfo) => {
    console.log('Selected ranges:', ranges);
    if (cellInfo) {
      console.log('Anchor element:', cellInfo.element);
      console.log('Bounding rect:', cellInfo.rect);
      // e.g. position a tooltip at cellInfo.rect.bottom, cellInfo.rect.left
    }
  }}
/>

Highlight Syntax

The highlight prop, setHighlight method, and getHighlight method all use Excel-style cell references:

| Expression | Meaning | |---|---| | A1 | Single cell | | A1:D10 | Rectangular range | | B:B | Entire column | | 3:3 | Entire row | | A1:B5, D1:E5 | Multiple ranges (comma-separated) |

Notes:

  • A plain single-cell click focuses the cell but does not create a visible highlighted range
  • setHighlight(''), clearHighlight(), or clearing the controlled highlight prop removes the current highlight

Edit Mode

Enable inline cell editing:

<SheetViewer
  source={file}
  mode="edit"
  onCellChange={(sheet, row, col, value) => {
    console.log(`${sheet}[${row},${col}] = ${value}`);
  }}
/>

Double-click a cell to start editing. Press Enter to confirm or Escape to cancel.


Copy & Paste with Formatting

  • Ctrl+C (Cmd+C on Mac) — copies the selected range as HTML (with inline styles) and TSV
  • Ctrl+V (Cmd+V on Mac) — pastes HTML (preferred, preserves styles) or plain TSV
  • Escape — clears the marching ants copy indicator

Paste requires mode="edit".


Large Cell Content

When a cell contains long text, inactive cells truncate with ellipsis. The formula bar always shows the complete value, and highlighted range anchor cells can expand to show full content.


Undo & Redo

  • Ctrl+Z (Cmd+Z) — undo
  • Ctrl+Y or Ctrl+Shift+Z — redo

Also available via ref:

if (ref.current?.canUndo()) ref.current.undo();
if (ref.current?.canRedo()) ref.current.redo();

Column & Row Resizing

Drag the right edge of column headers or the bottom edge of row headers to resize. Minimum column: 30px, minimum row: 20px.

Programmatic control:

ref.current?.setColumnWidth(0, 200);  // Column A → 200px
ref.current?.setRowHeight(0, 50);     // Row 1 → 50px

Cell Comments

Cells with comments display a red triangle in the top-right corner. Hover to see the comment. Manage via ref:

ref.current?.setCellComment('A1', 'Review this value', 'Alice');
const comment = ref.current?.getCellComment('A1');
ref.current?.setCellComment('A1', null);  // Remove

Conditional Formatting

const rules: ConditionalFormatRule[] = [
  {
    type: 'greaterThan',
    range: { startRow: 0, startCol: 0, endRow: 99, endCol: 0 },
    values: [100],
    style: { bgColor: '#e6f4ea', fontColor: '#137333' },
  },
  {
    type: 'colorScale',
    range: { startRow: 0, startCol: 1, endRow: 99, endCol: 1 },
    colorScale: ['#f4cccc', '#fce8b2', '#b7e1cd'],
  },
];

Supported rule types: greaterThan, lessThan, between, equalTo, textContains, top10, bottom10, colorScale, dataBar.


Data Validation

const validations: Record<string, ValidationRule> = {
  '0,0': { type: 'list', listItems: ['High', 'Medium', 'Low'] },
  '0,1': { type: 'number', min: 0, max: 100, errorMessage: 'Must be 0–100' },
};

Formula Engine

Parse and evaluate Excel-style formulas. 24 built-in functions:

| Category | Functions | |---|---| | Math | SUM, AVERAGE, COUNT, COUNTA, MIN, MAX, ABS, SQRT, POWER, ROUND | | Logical | IF, AND, OR, NOT | | Text | CONCATENATE, LEFT, RIGHT, MID, LEN, UPPER, LOWER, TRIM | | Lookup | VLOOKUP, HLOOKUP, INDEX, MATCH |


Types

All types are exported:

import type {
  SheetViewerProps,
  SheetViewerHandle,
  SheetViewerSource,
  SheetViewerMode,
  SheetViewerTheme,
  SheetData,
  CellRange,
  CellValue,
  CellComment,
  CellStyle,
  ChartOverlay,
  ChartSeries,
  ChartType,
  ConditionalFormatRule,
  ConditionalFormatRuleType,
  GridLineConfig,
  GridLineBorderStyle,
  ParsedGridLineConfig,
  MergeCell,
  SelectionState,
  UndoEntry,
  ValidationRule,
  ValidationRuleType,
} from 'rc-sheet-viewer-17';

Multiple Instances

Each <SheetViewer /> is fully isolated with its own state:

<div style={{ display: 'flex', gap: 16 }}>
  <SheetViewer source={file1} height={400} width="50%" />
  <SheetViewer source={file2} height={400} width="50%" />
</div>

Styling

Import the CSS once:

import 'rc-sheet-viewer-17/style.css';

All classes are scoped under .sheet-viewer and prefixed with sv-. Override CSS variables via the theme prop or directly in CSS:

.sheet-viewer {
  --sv-color-bg: #1e1e2e;
  --sv-color-primary: #89b4fa;
  --sv-search-match-color: rgba(137, 180, 250, 0.3);
  --sv-search-active-color: rgba(137, 180, 250, 0.6);
}

Development

Prerequisites

  • Node.js 18+
  • pnpm 8+

Commands

pnpm dev              # Start demo app (dev server)
pnpm build            # Build demo app
pnpm build:lib        # Build library package
pnpm typecheck        # TypeScript type checking
pnpm test             # Run all tests
pnpm test:watch       # Tests in watch mode
pnpm lint             # ESLint
pnpm storybook        # Storybook at localhost:6006
pnpm build-storybook  # Static Storybook build
pnpm chromatic        # Visual regression tests

Browser Support

Works in all modern browsers (Chrome, Firefox, Safari, Edge). No IE11 support.


License

MIT