--- title: Rendering a table from data updatedAt: 1717543017000 --- While building my [Todo page](../todo.md), I wanted to see if It'd be possible to parse the [todo.txt](https://github.com/todotxt/todo.txt) spec and then render a table from it. However, rendering _anything_ in the DOM from plain JS can get super bulk and messy really quickly, so I thought I'd write a cleaner, reusable utility to render out tables. I write all my scripts in Typescript, so first lets layout some basic data & types. We've got a Column generic Type that contains information about our columns. ```ts export interface Column { header: string; accessor: keyof T; renderer?: (value: T[keyof T], row?: T) => string; } ``` And then we have a basic footprint of our render function. ```ts export const renderTable = (args: { columns: Column[]; data: T[]; }): Element => {} ``` We're going to pass in an array of Objects, with the object type here being `T`. This function will be used accordingly: ```ts import {renderTable} from "./render-table"; interface ExampleDataInterface { name: string; age: number; } const exampleData: ExampleDataInterface = [ {name: "John Doe", age: 25, approved: false}, {name: "Jane Doe", age: 26, approved: true} ] const tableElement = renderTable({ columns: [ {header: 'Legal Name', accessor: 'name'}, {header: 'Users Age', accessor: 'age'}, { header: 'Approved', accessor: 'approved', renderer: (value) => value ? 'Yes' : 'No' } ], data: exampleData }) ``` Inside our render function we first want to destructure our arguments and create our basic elements. ```ts // Destructure our args into their own variables const { columns, data, sortable } = args; // Create the base table elements const wrapper = document.createElement('div'); const table = document.createElement('table'); const header = document.createElement('thead'); const body = document.createElement('tbody'); // Connect the base table elements together table.append(header); table.append(body); wrapper.append(table); ``` We now want to generate our header row where our Column titles will live. ```ts // Create the header row elements and apply a className const headerRow = document.createElement('tr'); headerRow.className = 'header'; // For our columns, add a th containing the header value columns.forEach((cell) => { const columnHeader = document.createElement('th'); columnHeader.innerText = cell.header; headerRow.append(columnHeader); }); // Append the header tr to the tables thead header.append(headerRow); ``` Next we want to generate our data rows. We can do this by iterating over each item in our `data` array and then iterating over each `column` ```ts data.forEach((cell) => { // Create a new row for this data const row = document.createElement('tr'); // For each column in the table columns.forEach((parentColumn) => { // Create a new cell const newCell = document.createElement('td'); // Set the cells inner text to either the output of the provided renderer, or the raw text newCell.innerText = `${ parentColumn.renderer ? parentColumn.renderer(cell[parentColumn.accessor], cell) : cell[parentColumn.accessor] }`; // Append the cell to the newly created row row.append(newCell); }); // Append the row to the tables tbody body.append(row); }); ```