No dependencies 174 kB gzipped Free for commercial use

The widgets business apps
actually run on — without the licence

A datagrid that sorts, filters and pages without another round trip. Draggable windows, comboboxes, a date picker, tabs, a layout frame. Plain JavaScript, one script tag, and nothing to install alongside it.

$npm i kob-ui
rent-roll.html

Why it exists

Built to replace jQuery EasyUI in a shipping product

EasyUI is GPL unless you buy a licence. Rewriting a working admin app around a component framework was not on the table either. So the widgets that app used were rewritten from scratch — and this is that code, cleaned up and made free to use — in commercial work, with no fee and no attribution.

Free, no strings

Use it in closed-source and commercial work. No dual licence, no per-developer fee, no attribution banner. It is not open source: the library ships as a build and may not be modified or republished.

Nothing else to load

No jQuery, no runtime, no polyfills. One script and one stylesheet, or an ES module import if you have a bundler.

A grid that does the work

Sorting, per-column filter chips, whole-table search, paging and inline editing all run locally on the rows you loaded.

Themed by variables

Every colour, radius and shadow is a CSS custom property. Change three of them and the whole library follows, light and dark.

Localised properly

English and Thai ship with it, Buddhist-era calendar included — while getValue() always hands you ISO dates.

Typed and documented

TypeScript definitions for every widget, option and method, plus a live example for each one on this site.

Getting started

Three lines to a working screen

From a script tag

No build step. window.Kob is ready as soon as the script runs.

<link rel="stylesheet" href="kob-ui/dist/kob-ui.css"> <script src="kob-ui/dist/kob-ui.min.js"></script> <div id="grid" style="height:400px"></div> <script> Kob.datagrid('#grid', { columns: [ { field: 'room', title: 'Room', width: 100 }, { field: 'tenant', title: 'Tenant', width: 180 }, { field: 'rent', title: 'Rent', width: 110, align: 'right' } ], url: '/api/rooms' }); </script>

As a module

ESM and CommonJS builds ship in the package, with types alongside.

import Kob from 'kob-ui'; import 'kob-ui/css'; const grid = Kob.datagrid('#grid', { columns, data: rows }); grid.sort('rent', 'desc');

Or straight from markup

Give an element the widget's class and let the parser build it.

<input class="kob-textbox" data-options="label:'Name',required:true"> <input class="kob-datebox" data-options="label:'Move in'"> <a class="kob-linkbutton icon-save"><span class="kob-icon"></span>Save</a> <script>Kob.parse(document);</script>

Live, on this page

Poke at it before you install anything

Every example below is running right now, and the code tab shows the exact source that produced it.

All the examples

Coming from EasyUI

The same shapes, without jQuery

If your app already speaks datagrid, dialog and combobox, the mental model carries over. Class names change from easyui-* to kob-*, and the jQuery plugin call becomes a plain function call that hands back the instance.

$('#dg').datagrid({ url: '/api/rooms', columns: [[ { field: 'room', title: 'Room', width: 100 }, { field: 'rent', title: 'Rent', width: 110, align: 'right' } ]] }); $('#dg').datagrid('reload'); var row = $('#dg').datagrid('getSelected'); $.messager.confirm('Delete', 'Sure?', function (yes) { if (yes) { remove(row); } });
const grid = Kob.datagrid('#dg', { url: '/api/rooms', columns: [ { field: 'room', title: 'Room', width: 100 }, { field: 'rent', title: 'Rent', width: 110, align: 'right' } ] }); grid.reload(); const row = grid.getSelected(); if (await Kob.confirm({ title: 'Delete', msg: 'Sure?' })) { remove(row); }
 kob-uijQuery EasyUI
LicenceFree for commercial useGPL, or a paid commercial licence
DependenciesNonejQuery
Payload477 kB JS + 67 kB CSS minified, 174 kB gzipped~250 kB plus jQuery, plus icon images
IconsMasked SVG in the stylesheetPNG sprites
Grid filteringPer-column chips, built inFilter row extension
ThemingCSS custom propertiesPrebuilt theme folders
Dark modeIncludedNo
TypeScript typesBundledCommunity definitions
Promisesawait Kob.confirm(…)Callbacks

What is in the box

Twenty-three widgets and the plumbing around them

datagrid

Sort, per-column filter chips, table-wide search, paging, row selection and inline editors.

desktop

A windowed shell: wallpaper icons, taskbar, start menu, one window per running app.

dialog · modal · window

Modal dialogs, or draggable and resizable application windows with minimise and maximise.

layout

north / west / center / east / south with draggable splitters and collapsible regions.

tree

Expand, select, tri-state checkboxes, and children fetched the first time a node opens.

ribbon

Tabs of grouped commands, large and small buttons, dropdown menus, folds away.

menu · menubar

Submenus, shortcuts, separators — as a menu bar, or bound to right-click anywhere.

tabs · panel

Tab pages from child elements; titled boxes that fold away or load HTML from a URL.

combobox · datebox

Value/text lists with keyboard navigation, and a calendar that reads and writes ISO dates.

textbox · numberbox · checkbox

Labelled inputs with clamping and thousands separators; a real checkbox drawn as a switch.

form

Read, fill, validate and clear every widget in a form with one call.

filterbutton

The grid's filter chip on its own, for filter bars that sit anywhere on the screen.

messager · alert

Promise-returning alert, confirm and prompt dialogs, corner toasts, and inline banners.

progressbar · preloader

Measured or indeterminate bars, spinners, and a one-line busy mask over anything.

linkbutton · badge

Toolbar buttons with 45 masked-SVG icons, and counts that cap and hide themselves.

card · grid · flex

Cards, button groups, a 12-column row and the flex helpers — CSS only, no JavaScript.

The whole shell

A windowed desktop, if that is the shape your app wants

Wallpaper icons, a taskbar, a start menu, and windows that drag, resize, minimise and maximise. Each one is an ordinary dialog with an ordinary widget inside it.

Make it yours

One accent variable repaints everything