README.md

February 13, 2026 · View on GitHub

Mundane UI

Mundane UI

The boring UI library.
Framework-agnostic, zero-dependency, lightweight UI component library.
Written in TypeScript, works everywhere — React, Vue, Angular, Svelte, or plain HTML.

npm version npm downloads license zero dependencies bundle size

Documentation · Getting Started · Why This Exists


Install

npm install mundane-ui

Quick Start

<link rel="stylesheet" href="mundane-ui/css/plain.css">
<div id="my-table"></div>
import { DataTable } from 'mundane-ui'
import 'mundane-ui/css/plain.css'

const table = DataTable.create({
  el: '#my-table',
  mode: 'frontend',
  data: [
    { name: 'Alice', age: 30, email: 'alice@example.com' },
    { name: 'Bob', age: 25, email: 'bob@example.com' },
  ],
  columns: [
    { key: 'name', label: 'Name', type: 'string', sortable: true },
    { key: 'age', label: 'Age', type: 'number', sortable: true, thousandSeparator: true },
    { key: 'email', label: 'Email', type: 'string', sortable: true },
  ],
})

Features

  • Zero dependencies — pure vanilla TypeScript
  • Framework-agnostic — plain JS class, mounts to any DOM element
  • Two modes — frontend (client-side) and backend (server-side with QSP emission)
  • Sorting — single-column, per-type comparators (string, number, date, custom)
  • Search — global search, column filters, or both (debounced)
  • Pagination — configurable page sizes, ellipsis for large page counts
  • Column alignment — left/center/right per column (numbers default to right)
  • Number formatting — optional thousand separator (e.g. 1,000,000)
  • Date parsing — built-in parser for common date formats (YYYY, MM, DD, HH, mm, ss)
  • Custom columns — render any HTML with your own function
  • XSS safe — all data values are HTML-escaped
  • CSS customizable — CSS custom properties, class overrides, or Tailwind utility classes
  • Lightweight — ~9KB gzipped

Frontend Mode

All data is held in memory. Sorting, filtering, and pagination happen client-side.

const table = DataTable.create({
  el: '#table',
  mode: 'frontend',
  data: myData,
  pageSize: 25,
  search: { enabled: true, mode: 'both', debounce: 300 },
  columns: [
    { key: 'id', label: 'ID', type: 'number', sortable: true, align: 'center' },
    { key: 'name', label: 'Name', type: 'string', sortable: true },
    { key: 'salary', label: 'Salary', type: 'number', sortable: true, thousandSeparator: true },
    { key: 'joinDate', label: 'Joined', type: 'date', dateFormat: 'DD/MM/YYYY', sortable: true },
    {
      key: 'actions', label: 'Actions', type: 'custom',
      render: (row) => `<button onclick="edit(${row.id})">Edit</button>`,
    },
  ],
})

Backend Mode

The component does not sort/filter/paginate. It emits query string parameters on every state change, and you fetch the data however you want.

const table = DataTable.create({
  el: '#table',
  mode: 'backend',
  data: initialPageData,
  totalRows: 500,
  columns: [ /* ... */ ],
  onStateChange: async (queryString, state) => {
    // queryString: "?page=2&pageSize=10&sortBy=name&sortOrder=asc&search=john"
    const res = await fetch('/api/users' + queryString)
    const { data, total } = await res.json()
    table.setData(data, total)
  },
})

Column Types

TypeDefault AlignSortableSearchableOptions
stringleftlocaleComparesubstring match
numberrightnumerictoString matchthousandSeparator
dateleftDate comparisondisplay string matchdateFormat, displayFormat
customleftvia comparatorvia filterFnrender

CSS Customization

Layer 1 — Theme file:

import 'mundane-ui/css/plain.css'

Layer 2 — CSS custom properties:

.mu-datatable {
  --mu-color-border: #e2e8f0;
  --mu-color-bg-header: #f8fafc;
  --mu-border-radius: 8px;
}

Layer 3 — Direct class overrides:

.mu-datatable__header-cell {
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

Layer 4 — Tailwind via classes config:

DataTable.create({
  classes: {
    root: 'rounded-xl border border-gray-200 shadow-sm',
    headerCell: 'bg-gray-50 text-xs font-semibold uppercase',
    row: 'hover:bg-blue-50 transition-colors',
    pageButtonActive: 'bg-blue-600 text-white',
  },
  // ...
})

Public API

MethodDescription
setData(data, totalRows?)Replace dataset, triggers re-render
updateRow(index, rowData)Update a specific row
addRow(rowData, index?)Add a row
removeRow(index)Remove a row
setPageSize(size)Change page size (resets to page 1)
goToPage(page)Navigate to a page
setSearch(value)Set global search programmatically
setColumnFilter(key, value)Set a column filter
clearFilters()Clear all filters and search
setLoading(bool)Show/hide loading overlay
getState()Get current state object
getQueryString()Get current query string
setColumns(columns)Update column definitions
destroy()Remove DOM and event listeners
refresh()Force full re-render

License

MIT