# revogrid: blank is a configurable policy object, and Excel export is not in the box

> A virtual data grid built with StencilJS that claims a million rows with no hard row limit and ships for five frameworks from one codebase, with three tiers of export and a filter system whose blank semantics are specified down to inherited-property handling and parser interaction. The licence badge points at a branch the project no longer uses, and two formatter configs sit side by side at the root.

**revolist/revogrid** — Powerful virtual data table smartsheet with advanced customization. Best features from excel plus incredible  performance 🔋

- Repository: https://github.com/revolist/revogrid
- Website: https://rv-grid.com
- Stars: 3,448 · Forks: 210
- Language: TypeScript
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/revolist-revogrid

## Blank is a policy object specified down to inherited properties

The filtering section contains the most precisely written paragraph in the file, and it is about what counts as blank.

Blank filters preserve source-value identity and own-property presence. By default four things are blank: null, an owned undefined, exactly an empty string, and a missing own property. Six things are not: whitespace-only strings, false, zero, not-a-number, arrays and objects. That list is short enough to check against your data model, which is unusual in a feature list.

The policy is configurable at grid level and overridable field by field with a per-column object, and the override can be partial:

```ts
const filter = {
  blankSemantics: {
    whitespaceOnlyString: true,
    emptyArray: true,
    isBlank(value, context, fallbackResult) {
      return value === 'N/A' || fallbackResult;
    },
  },
};

const columns = [{
  prop: 'tags',
  filter: 'array',
  // Partial column policy; other fields inherit the grid policy.
  blankSemantics: { emptyArray: false },
}];
```

Three further rules close it. An inherited property counts as missing. A cell parser supplies the value for ordinary comparisons but does not replace the raw value used for blank checks, so parsing cannot silently change what is blank. And the not-blank operator is the exact inverse of the resolved predicate, with the older operator identifiers still compatible and relabelled.

## Three export tiers, and only the first one is built in

Export is listed with three targets and three different levels of access.

CSV is described as built-in file export for the core data workflows. PDF is browser-side and requires a separate package published under the same scope, so it is an add-on rather than a build flag. Excel is marked as a Pro feature, and the description is specific about what it carries: layout, styles, frozen panes, merged cells and formulas.

That ordering is the practical one to plan against. A grid that renders a million rows and cannot produce a workbook out of the box is not unusual, but it does mean the headline feature list overstates what an unlicensed user can do, and the anchor for the Pro section sits in the header navigation next to the key features link.

The rest of the feature list is in the box, including filtering with custom filters that extend the system set, grouping with nested headers and a drill-down mode, pinned rows and columns on any edge, four column types plus a custom template type, theme packages, trimmed rows for hiding on demand, and a plugin system.

## One component, five frameworks, and a standalone build with no CommonJS entry

The grid is built with StencilJS, which is how a single TypeScript codebase ends up shipping to five frameworks. The package keywords list React, Vue, Vue 3, Svelte and Angular, each with grid and datagrid variants alongside them, plus a spread of feature words from CSV and pivot to sparkline and treeview.

The export map shows what that costs in configuration. The root entry resolves types, an ES module and a CommonJS build. The standalone entry resolves types and an ES module only. Two further standalone subpaths exist for the grid script and for a filter panel script.

So the CommonJS story is uneven. A CommonJS application can require the root entry and gets the compiled CommonJS file, but the standalone build, the one intended for a page without a bundler, has no require condition at all.

There are also two Stencil-specific collection fields and an unpkg entry pointing at a separate ESM file under a revo-grid directory, so the package exposes at least five distinct built artefacts.

## The licence badge points at a branch the project no longer uses

The default branch is main. The licence badge in the header links to the licence file on the master branch.

That is the whole finding, and it is a common enough residue of a branch rename that it is worth naming rather than dismissing. The link either resolves to nothing or resolves to a stale copy of the licence, depending on whether master still exists.

It sits among five other header links, of which the other four are current: the project site, two package registry listings, and the build status for two workflows. One of those workflows is named for unit tests and the other for end-to-end tests, so both halves of the suite gate the badge that is broken.

Everything else about the packaging is current. The manifest version matches the newest tag, the licence field in the manifest matches the badge, and the author, homepage and repository fields all point at the same places the header does.

## Two formatter configs and a Playwright config named after a port

The root of the repository carries a pair of formatter configurations, one with a JavaScript extension and one with a JSON extension.

Prettier resolves one of them and ignores the other, and which one depends on its own precedence rules rather than on anything the project states. So a contributor can reasonably believe they have edited the configuration in force and have changed nothing.

The test configuration has a similar shape. There is a Playwright config, and beside it a second one whose filename ends in a specific local port number, which suggests a variant kept for running the end-to-end suite against a development server on a fixed port rather than the managed one.

Two test directories back that up: one for end-to-end and one named simply test. There is a typedoc configuration as well, so the API reference is generated from source, alongside a documentation directory and a script directory. And a sonarcloud properties file, which is the only sign in the tree of static analysis.

## The readme exists as both a file and a directory

Among the top-level entries there is a readme file in lower case and, separately, a readme directory. The documentation site content and the source of the readme are therefore both in the tree, which is a normal arrangement for a project that publishes translations or multiple versions.

What is less explained is the naming. The default branch is main, the repository is TypeScript, and the file that the site renders is the same document the header links to, so the two have to be kept in step by something. The presence of an editor config, an agent instructions file and a script directory suggests a script does it, but nothing in the visible text says which one is authoritative.

One other entry is formatted differently from its neighbours. The documentation directory appears without a trailing slash while most other directories have one, which is cosmetic unless a script is globbing those paths.

None of this is a defect on its own. It is the accumulation that makes a first-time reader wonder which file to edit.

## The performance claim appears twice, backed by three overlapping mechanisms

The headline states that a million rows or more, millions of cells and thousands of columns can be rendered with no hard row limit, and the same claim is repeated as the first item in the feature list.

Three separate bullets then describe the mechanism, and they overlap. Virtualization keeps the DOM focused on the visible viewport. Virtual scroll handles large datasets without rendering every row or column into the DOM. And an intelligent virtual DOM is described as doing smart row recombination to minimise redraws.

The third is the interesting one, because row recombination is the part that reduces redraws when data changes rather than the part that keeps the DOM small. So the mechanism is presented three times at three levels of specificity, with the most technical version last.

Alongside that sits an unattributed adoption claim, that the grid is used by some of the largest companies in Europe and the United States, with no customer named and no measurement given. It is the only sentence in the file with no link attached to it.

## Conclusion

revogrid is worth evaluating if you need spreadsheet behaviour rather than a table, since clipboard compatibility with Excel and Google Sheets, frozen rows and columns, and per-column filtering are the parts that are expensive to rebuild. Four things to check first. Export is tiered: CSV is built in, PDF is a separate package, and Excel workbooks are a paid feature, so price your requirement against the tier rather than the feature list. Blank filtering has a real policy object behind it, which means you can decide that a whitespace string or an empty array is blank, but the default is deliberately strict and existing saved filters keep working. The standalone bundle has no CommonJS entry, so a CommonJS application cannot use that path. And the repository has two prettier configs at the root, so find out which one is actually applied before you fight the formatter.

## FAQ

### Is there anything better than Ag Grid?

The repository does not compare itself to any named product. It lists its own features: virtual scrolling with no hard row limit, WAI-ARIA accessibility, right-to-left support, Excel-like keyboard and clipboard behaviour, per-column filtering, grouping, pinned rows and columns, theme packages, and a plugin system.

### revo grid vs ag grid

Nothing in the repository addresses the comparison. What it states about itself is that it renders a million rows or more with no hard row limit, that it is built with StencilJS, and that it ships for React, Vue, Svelte and Angular from one codebase.

### revogrid vs tanstack table

The repository names no alternative and makes no comparison. Its own position is a spreadsheet component with virtual scrolling, a plugin system, theme packages and clipboard behaviour compatible with Excel and Google Sheets.

### Can RevoGrid export to Excel, PDF and CSV?

All three are listed, at three different levels. CSV export is built in, PDF export is a separate published plugin package, and Excel workbook export with layout, styles, frozen panes, merged cells and formulas is a Pro feature.

### How do I change what RevoGrid treats as blank when filtering?

Blank semantics are configurable at grid level and overridable per column with a blankSemantics object. By default null, an owned undefined, exactly an empty string and missing own properties count as blank, while whitespace-only strings, false, zero, not-a-number, arrays and objects do not.

## Sources

- [License: MIT](https://github.com/revolist/revogrid/blob/main/LICENSE)
- [Project website](https://rv-grid.com)
- [README](https://github.com/revolist/revogrid/blob/main/README.md)
- [Releases](https://github.com/revolist/revogrid/releases)
- [revolist/revogrid on GitHub](https://github.com/revolist/revogrid)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/revolist-revogrid
