demo D109
Data Masking and Column Redaction in htmx
Masking values a viewer must not see
grid.redaction
This masks the contents of specific cells from a viewer while leaving the row and column structure intact. It lets someone work with sensitive records, scanning a customer list, without ever rendering the card or ID number underneath.
This is the htmx version. A server-rendered table is upgraded in place, so the page is readable before any script runs and gains the full grid when the module loads, which suits a progressively enhanced app.
The configuration
<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.min.css">
<!-- A real, server-rendered table: readable with JavaScript off. data-field on
each header names the column that cell becomes, and data-id on each row is
the key the grid carries it under. -->
<table id="redaction-table" data-lattice-grid>
<thead>
<tr>
<th data-field="service">Service</th>
<th data-field="team">Team</th>
<th data-field="owner">Owner</th>
<th data-field="region">Region</th>
<th data-field="headcount">Headcount</th>
<th data-field="monthlyCost">Monthly cost</th>
<th data-field="budget">Budget</th>
</tr>
</thead>
<tbody>
<tr data-id="1"><td>svc-00001</td><td>Search</td><td>k.yilmaz</td><td>Malmö</td><td>2</td><td>37920.55</td><td>20036</td></tr>
<tr data-id="2"><td>svc-00002</td><td>Mobile</td><td>m.svensson</td><td>Leeds</td><td>2</td><td>30070.04</td><td>56591.85</td></tr>
<tr data-id="3"><td>svc-00003</td><td>Identity</td><td>j.okafor</td><td>Zürich</td><td>1</td><td>36278.48</td><td>8543.17</td></tr>
<!-- the rest of the rows, rendered by your server -->
</tbody>
</table>
<script type="module">
import { hydrateTable } from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/modules/htmx.esm.min.js';
// The markup carries the rows; a server-rendered table's column types are
// declared in the config hydrateTable takes. The grid replaces the table
// where it stood.
const grid = hydrateTable(document.getElementById('redaction-table'), {
selection: 'multiple',
toolPanel: {
side: 'left',
panels: ['columns', 'filters', 'views', 'quick'],
actions: ['undo', 'redo', 'export', 'restore', 'maximise'],
exportName: 'lattice-demo',
},
// Redaction is grid state, which is why it can be seeded here at all: it
// travels in a saved view and undoes like any other change rather than
// being a switch outside the model.
state: { version: 1, redaction: ['monthlyCost', 'budget'] },
columns: [
{ field: 'service', title: 'Service', layout: { pin: 'start', width: 160 } },
{ field: 'team', title: 'Team', filter: { type: 'set' } },
{ field: 'owner', title: 'Owner', filter: { type: 'set' } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'headcount', title: 'Headcount', type: 'number', total: 'sum' },
{ field: 'monthlyCost', title: 'Monthly cost', type: 'number', format: { style: 'currency', currency: 'USD' }, total: 'sum' },
{ field: 'budget', title: 'Budget', type: 'number', format: { style: 'currency', currency: 'USD' }, total: 'sum' },
],
});
</script>
Masking values a viewer must not see
Redaction hides the contents of specific cells from a viewer while leaving the row and column structure intact, so a support agent can scan customer records without ever rendering the card number or national insurance number underneath the mask. It sits below permissions in the access model: permissions decide whether a role can see a column at all, while redaction handles the case where a role can see that a value exists but not the value itself. A developer reaches for it wherever regulation or internal policy demands that data be visible to some sessions and masked for others without shipping two different views of the same JavaScript data grid. Lattice Grid exposes this through grid.redaction, which a host wires to its own entitlement check per cell so the masking decision is made once per render pass rather than recomputed on every scroll event. Masked cells still occupy their row height and column width, so a viewer’s layout does not shift when a value is hidden, and screen readers announce the mask state rather than reading a blank cell as empty data. Because the check runs against the currently rendered viewport, applying or lifting a redaction rule across thousands of rows costs a lookup per visible cell, not a pass over every record in the grid.
How do you mask sensitive data in a JavaScript data grid?
Data masking in Lattice Grid runs through grid.redaction, which a host application uses to mark specific cells as hidden from the current session based on its own entitlement logic. The grid renders a mask in place of the value, preserves the row and column layout, and reports the mask state to assistive technology, so a viewer without clearance sees that a field exists without ever receiving its contents.