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

structured-table

v0.3.0

Published

Structured, themeable, SSR-friendly tables for React and Sanity.

Readme

structured-table

Type-safe, headless, SSR-friendly table engine for React, Vue, and Svelte.

This is the core package of the Structured Table project. It provides:

  • An STL parser that converts a Structured Table Language string into a typed SanityTable object
  • A stringifier that converts a SanityTable back into STL
  • A renderer registry (registerRenderer / getRenderer) used by framework renderers to plug in without coupling to the core

Full documentation & playground: stl-table.vercel.app

Installation

npm install structured-table
# or
pnpm add structured-table
# or
yarn add structured-table

Quick Start

import { STL } from "structured-table";

const stl = `
#table
name: Product Catalog
cols: 3

[header]
Product | Price | In Stock

[body]
Laptop | $999 | Yes
Mouse | $29 | Yes

[footer]
Total {colSpan=2} | 2 items

#endtable
`;

const table = STL.parse(stl);
// → SanityTable { header, body, footer, ... }

API

STL.parse(stlString)

Parses an STL string into a SanityTable object.

import { STL } from "structured-table";

const table = STL.parse(stlString);

STL.stringify(table)

Converts a SanityTable object back into an STL string.

import { STL } from "structured-table";

const stlString = STL.stringify(tableObject);

registerRenderer(kind, renderer) / getRenderer(kind)

Used by framework renderers (React, Vue, Svelte) to register themselves. You typically call this once in your renderer's register.ts file and then call getRenderer inside your table component.

import { registerRenderer, getRenderer } from "structured-table";

// In your renderer's register file
registerRenderer("react", { Table: MyTableComponent });

// In a component that needs to render
const { Table } = getRenderer("react");

STL Format

STL (Structured Table Language) is a concise, human-readable syntax for defining tables.

Basic Structure

#table

[header]
Column 1 | Column 2 | Column 3

[body]
Row 1, Cell 1 | Row 1, Cell 2 | Row 1, Cell 3
Row 2, Cell 1 | Row 2, Cell 2 | Row 2, Cell 3

[footer]
Footer Cell 1 | Footer Cell 2 | Footer Cell 3

#endtable

Sections are optional — you can have [body] only, or any combination of [header], [body], [footer].

Table-Level Options

Place these at the top before any section:

name: My Table
cols: 3
showSerialIndex: true

[header]
...

| Option | Type | Default | Description | |--------|------|---------|-------------| | name | string | "" | Table name / accessible label | | cols | number | auto | Expected column count | | showSerialIndex | boolean | false | Prepend a row-number column |

Cell Attributes

Add attributes to any cell using {key=value} or {key="value"} syntax:

[body]
Merged Cell {colSpan=2} | Third Column
Centered {align="center"} | Left | Right {align="right"}
Row header {cellType="header"} | Data | Data
Styled cell {class="highlight"} | Normal | Normal

| Attribute | Values | Description | |-----------|--------|-------------| | colSpan | number | Span N columns horizontally | | rowSpan | number | Span N rows vertically | | align | left | center | right | Text alignment | | cellType | header | data | Semantic rendering hint (<th> vs <td>) | | class | string | CSS class(es) passed to the rendered cell element |

Inline Content

Text cells support inline HTML nodes inside the cell value. Currently supported:

Line break ([br])

[body]
Line one[br]Line two | Normal cell
First paragraph[br]Second paragraph | Another cell

The parser converts [br] into an InlineHtmlNode ({ type: "html", tag: "br" }). Renderers are expected to convert inline nodes to their HTML equivalents.

Rich Cells (Links & Buttons)

[body]
User Name | [link text="View Profile" href="/users/1"]
Document  | [link text="Open" href="https://example.com" newTab="true"]
Order #42 | [button text="Cancel" targetId="order-42" action="cancel-order" variant="ghost"]
Product   | [button text="Buy Now" url="/checkout" variant="default"]

Link attributes: text, href, newTab

Button attributes: text, url, action, targetId, variant (default | outline | ghost)

TypeScript Types

All types are exported from the package root:

import type {
  SanityTable,
  TableRow,
  TableCell,
  TextCellProps,
  LinkCellProps,
  ButtonCellProps,
  InlineNode,
  InlineTextNode,
  InlineHtmlNode,
  SanityTableProps,
} from "structured-table";

Key types

interface SanityTable {
  name: string;
  caption?: string;
  cols: number;
  showSerialIndex: boolean;
  header?: TableRow;
  body: TableRow[];
  footer?: TableRow;
}

interface TableRow {
  cells: TableCell[];
  uid: string;
}

// TableCell = TextCellProps | LinkCellProps | ButtonCellProps

interface TextCellProps extends TableCellBase {
  type: "text";
  value: string | InlineNode[]; // InlineNode[] when inline elements like [br] are present
}

// InlineNode = InlineTextNode | InlineHtmlNode
interface InlineHtmlNode {
  uid: string;
  type: "html";
  data: string;
  tag: string; // e.g. "br"
}

Framework Renderers

The core package has no UI. Framework renderers are installed via the CLI:

npx stl-cli add react    # React renderer
npx stl-cli add vue      # Vue renderer
npx stl-cli add svelte   # Svelte renderer

Each renderer scaffolds component files into your project and exports a register.ts that calls registerRenderer. See the renderer docs for full setup instructions.

License

MIT © Yashraj Yadav