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

react-xel

v0.2.1

Published

Production-focused Next.js/React Excel exporter with native OOXML writer.

Readme

react-xel

Advanced Excel export toolkit for Next.js and React applications, with a native writer (no xlsx dependency).

Features

  • Multi-sheet exports with per-sheet options
  • Column formulas, number formats, and style hooks
  • Header freeze, auto filter, row heights, merges, hidden columns
  • Template archive validation guards (zip safety limits)
  • Formula injection sanitization for user-provided strings
  • Typed error classes for validation/security/writer failures
  • .xlsx, .csv, and .txt output
  • Buffer/blob/download modes for browser and server
  • Next.js response helper (toAttachmentResponse)
  • Delimited string exporter (exportDelimited)
  • TanStack column mapper (mapTanStackColumns)
  • Advanced adapters (mapTanStackColumnsAdvanced, mapFormFieldsToColumns)
  • UI primitives: ExcelExportButton, ExcelExportMenu, useExcelExportTask
  • Extension lifecycle hooks with deterministic priority ordering
  • Headless UI mode + tokenized CSS variables for design systems

Installation

pnpm add react-xel

Quick client export

"use client";

import { exportExcel } from "react-xel";

export function DownloadButton() {
  const onClick = () => {
    exportExcel({
      fileName: "sales-report.xlsx",
      rows: [
        { id: 1, customer: "Alice", total: 1200, createdAt: new Date() },
        { id: 2, customer: "Bob", total: 980, createdAt: new Date() }
      ],
      sheetName: "Sales",
      metadata: {
        title: "Sales Report",
        author: "Dashboard App"
      }
    });
  };

  return <button onClick={onClick}>Download Excel</button>;
}

Advanced workbook (multi-sheet + formulas + styles)

import { exportExcel } from "react-xel";

exportExcel({
  fileName: "analytics.xlsx",
  sheets: [
    {
      sheetName: "Revenue",
      rows: [
        { month: "Jan", gross: 15000, feeRate: 0.05 },
        { month: "Feb", gross: 22000, feeRate: 0.05 }
      ],
      columns: [
        { header: "Month", key: "month", width: 14 },
        { header: "Gross", key: "gross", numberFormat: "#,##0.00" },
        { header: "Fee %", key: "feeRate", numberFormat: "0.00%" },
        {
          header: "Net",
          formula: (_, rowIndex) => `B${rowIndex + 2}*(1-C${rowIndex + 2})`,
          numberFormat: "#,##0.00"
        }
      ],
      autoFilter: true,
      freezeHeaderRow: true,
      headerStyle: { font: { bold: true } },
      rowHeights: [24, 20, 20]
    },
    {
      sheetName: "Users",
      rows: [
        { plan: "Free", count: 2300 },
        { plan: "Pro", count: 410 }
      ],
      columns: [
        { header: "Plan", key: "plan", width: 18 },
        { header: "Users", key: "count", width: 12, numberFormat: "#,##0" }
      ],
      merges: ["A1:B1"]
    }
  ],
  metadata: {
    title: "Business Snapshot",
    subject: "Monthly KPIs",
    author: "Ops Team",
    company: "Acme Inc."
  }
});

Server route example (Next.js)

import { exportExcel, toAttachmentResponse } from "react-xel";

export async function GET(): Promise<Response> {
  const buffer = exportExcel({
    fileName: "server-report.xlsx",
    rows: [
      { id: 1, status: "ok" },
      { id: 2, status: "queued" }
    ],
    mode: "buffer"
  });

  if (!buffer) {
    throw new Error("Expected buffer output.");
  }

  return toAttachmentResponse(buffer, "server-report.xlsx");
}

Notes

  • This version writes .xlsx directly from generated OOXML + ZIP.
  • Date values are serialized as Excel date numbers (not ISO text).
  • templateBuffer is validated for archive safety but full template mutation is not implemented yet.

UI package

Import from react-xel/ui.

  • ExcelExportButton
  • ExcelExportMenu
  • ExcelExportPanel
  • useExcelExportTask

Import default styles once (for polished built-in look):

import "react-xel/styles.css";
"use client";

import { ExcelExportButton } from "react-xel/ui";

export function UsersExportButton() {
  return (
    <ExcelExportButton
      label="Export Users"
      fileName="users.xlsx"
      rows={[
        { id: 1, name: "Ada" },
        { id: 2, name: "Linus" }
      ]}
      sheetName="Users"
    />
  );
}

Core API

  • exportExcel(options)
  • createWorkbook(options)
  • workbookToBuffer(workbook, bookType?)
  • arrayBufferToBlob(buffer, mimeType?)
  • toAttachmentResponse(buffer, fileName, mimeType?)
  • exportDelimited({ rows, columns, delimiter })
  • mapTanStackColumns(columns)
  • mapTanStackColumnsAdvanced(columns)
  • mapFormFieldsToColumns(fields)
  • createNextRouteHandler(buildOptions)
  • createExtension(extension)
  • defineColumns(columns)
  • defineSheets(sheets)
  • definePreset(preset)
  • defineExtension(extension)
  • composeExportOptions(...presets)
  • useExcelExport(defaults?)
  • ValidationError, WriterError, TemplateError, SecurityError

Performance benchmarks

Generate baseline performance metrics:

pnpm benchmark:json

Results are written to benchmarks/latest.json and include wall-clock and memory deltas.

Extension model

See enterprise extension lifecycle guarantees in docs/extension-model.md.

Error handling

import { ValidationError, exportExcel } from "react-xel";

try {
  exportExcel({ fileName: "bad.ext", rows: [{ id: 1 }] });
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(error.code, error.message);
  }
}

Testing

Run tests with:

pnpm test

Run only UI component tests:

pnpm test:ui:run

Watch mode:

pnpm test:watch

Vitest UI:

pnpm test:ui

Coverage:

pnpm test:coverage

UI testing guide

1) Automated UI tests

This package already includes component tests in tests/ui.test.tsx for:

  • ExcelExportButton click behavior
  • ExcelExportMenu rendering
  • disabled state behavior

Run them with:

pnpm test:ui:run

Interactive runner:

pnpm test:ui

2) Manual UI verification in a Next.js app

Create a client page/component using react-xel/ui:

"use client";

import { ExcelExportButton, ExcelExportMenu } from "react-xel/ui";

export default function Page() {
  return (
    <div style={{ display: "grid", gap: 12 }}>
      <ExcelExportButton
        label="Export Users"
        fileName="users.xlsx"
        rows={[{ id: 1, name: "Ada" }]}
      />

      <ExcelExportMenu
        items={[
          {
            id: "users",
            label: "Users Report",
            options: { fileName: "users.xlsx", rows: [{ id: 1, name: "Ada" }] }
          },
          {
            id: "sales",
            label: "Sales Report",
            options: { fileName: "sales.xlsx", rows: [{ month: "Jan", total: 1200 }] }
          }
        ]}
      />
    </div>
  );
}

Manual checklist:

  • Button click downloads users.xlsx
  • Menu selection changes export payload
  • Disabled menu/button cannot trigger export
  • File opens in Excel/Sheets with expected headers and rows

Monorepo workspace

This repository is configured as a pnpm workspace with a documentation site (not only demos):

  • apps/docs: Next.js 16 documentation app using Fumadocs (MDX content, search, layouts). It consumes react-xel via workspace:*. The site lives under /docs (the root URL redirects there).

Run the documentation UI:

pnpm install
pnpm dev:docs

Then open http://localhost:3000 (or http://localhost:3000/docs directly).

License

MIT