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

json-renderers-build-table

v1.22.2

Published

Focused table specification builder with configurable rows and cells

Downloads

550

Readme

json-renderers-build-table

Build a complete HTML table JSON specification from columns + data — declaratively.


Why this exists

Building a <table> from raw data is repetitive work that quickly becomes messy:

  • You scatter createElement calls across your codebase
  • Adding a column means touching the header, the body cells, the footer, and the colgroup
  • Swapping a cell style for one column (e.g. a badge instead of plain text) means rewriting imperative DOM code
  • Testing is hard because the logic is entangled with the DOM

This library inverts that. You describe what the table should look like in JSON. The library stamps it out.

The result is a plain JSON object — a DOM specification — that you hand to a renderer like @keshavsoft/json-to-tag. The renderer turns it into real DOM elements. The builder never touches the DOM.


The core idea: declarative instructions over a shared registry

Every build runs through three layers:

1. Registry      holds { data, base, spec }
                   data  = your columns + rows
                   base  = the empty table skeleton (colgroup, thead, tbody, tfoot)
                   spec  = named cell/row templates

2. Instructions  a JSON array that says:
                   "read THIS source path,
                    apply THIS template,
                    write the result to THIS destination"

3. writePath     navigates the base tree by data-role anchor
                 and writes using a named action
                 (set / push / unshift / pushToRows / ...)

Because the instructions are JSON, you can read a build sequence without tracing any JavaScript. Each instruction has a narration field that explains itself in plain English.


How a table gets built (v18 simple)

columns.json     batches.json
      \               /
       simple({ columns, data })
              |
              |  registry = { data, base, spec }
              |
              +-- single instructions (fromCollection)
              |     colgroup children   (one <col> per column)
              |     thead > tr children (one <th> per column)
              |
              +-- double instructions (twoDimensional)
                    tbody children      (one <tr> per row, one <td> per column)
              |
              +-- returns base (fully populated JSON tree)

The returned base is a plain JavaScript object:

{
  "tagName": "table",
  "attributes": { "class": "table table-hover ..." },
  "children": [
    { "tagName": "colgroup", "children": [ "..." ] },
    { "tagName": "thead",    "children": [ { "tagName": "tr", "children": [ "..." ] } ] },
    { "tagName": "tbody",    "children": [ "..." ] },
    { "tagName": "tfoot",    "children": [ "..." ] }
  ]
}

Hand that to any spec-to-DOM renderer and you get a real table element.


Quick start

import { simple } from "./docs/dist/v18/min.js";

const columns = [
    {
        columnName: "itemName",
        width: "300px",
        template: { body: { tr: "buildBodyTr", cell: "buildBodyCell" } }
    },
    {
        columnName: "baseUnit",
        width: "200px",
        template: { body: { tr: "buildBodyTr", cell: "buildBodyCell" } }
    },
    {
        columnName: "batches",
        width: "100px",
        template: { body: { tr: "buildBodyTr", cell: "buttonCell" } }
    }
];

const data = [
    { itemName: "0.11-25", baseUnit: "kgs", batches: 1 },
    { itemName: "0.11-30", baseUnit: "kgs", batches: 0 }
];

const spec = simple({ columns, data });

// spec is a plain JSON tree — pass it to your DOM renderer
const element = window.ks.jsonToTag.buildSpecElement(spec);
document.getElementById("container").appendChild(element);

What each column config means

| Field | Purpose | |---|---| | columnName | Maps to the data property on each row object | | width | Written into the <col> element in the colgroup | | template.body.tr | Key into spec.json — which <tr> template to use for body rows | | template.body.cell | Key into spec.json — which <td> template to use for this column's cells |

You change the look of a column's cells by pointing template.body.cell at a different key in spec.json. No JavaScript changes needed.


Cell templates (spec.json)

Pre-built templates in src/v18/spec.json:

| Key | Renders | |---|---| | buildBodyCell | Plain <td> with the row value as text | | buttonCell | <td> containing a <button> showing the aggregate count | | buildHeader | <th> with uppercase text | | buildColGroup | <col> with a width attribute | | buildFooter | <td> with a text <input> for footer rows | | buildOption | <option> element | | buildSelect | <select> container | | buildDataList | <datalist> container |


Why columns drive everything

A column definition is the single source of truth for one vertical slice of the table:

  • its header cell (buildHeader template)
  • its width (buildColGroup template)
  • its body cell template (template.body.cell)
  • its footer cell (if enabled)

Adding a column = adding one object to the columns array.
Removing a column = removing it.
The instructions loop over columns automatically.
There is no imperative "add a th, then add a td, then add a col" sequence to maintain.


Build

npm install
npm run build
# output: docs/dist/v18/min.js

The Vite config auto-detects the highest vN folder under src/ and builds that version. No config changes needed when a new version is added.


Run the sample

npx serve
# open http://localhost:PORT/samples/v18/simpleTable/

Project structure

src/
  v18/
    index.js              exports { simple }
    spec.json             named cell/row templates
    simple/
      index.js            the build runner (registry + instructions)
      base.json           empty table skeleton with data-role anchors
      instructions/
        single.json       fromCollection instructions (colgroup, thead)
        double.json       twoDimensional instruction (tbody)
      writePath/
        index.js          tree writer with named actions

samples/
  v18/
    simpleTable/          live demo: columns.json + batches.json

docs/
  dist/
    v18/min.js            pre-built bundle (committed, served via GitHub Pages)