The Unsolved Primitives of UI Engineering

Every problem on this list shares the same pattern: dozens of libraries attempt to solve it, each one excels at something the others can't do, and none of them owns the complete solution. The quantity of competing approaches is the evidence that the root problem remains open.

These are not feature requests. These are missing primitives — fundamental layers of computation that are currently trapped inside black boxes (browsers, proprietary software, framework-coupled libraries) and have never been extracted as pure, universal, framework-agnostic engines.

The test for inclusion: if the primitive were extracted as pure arithmetic with no DOM, no framework, and no side effects, would it collapse the need for multiple competing libraries? If yes, it belongs here.

There is a deeper reason this matters. Every primitive on this list is currently locked inside the browser's rendering pipeline. That means canvas-based applications can't use them. Video renderers can't use them. Native platforms reimplement them from scratch. WebGL, server-side rendering, embedded systems, AI-generated interfaces — none of them have access to these primitives. Extracting them doesn't just unify competing web libraries. It makes an entire class of computation portable to any platform that can run arithmetic.

Reference case: Text Layout & Measurement. The browser knows how to lay out text but never let you control it programmatically. The primitive was recently extracted: segment text, measure once via canvas, lay out at any width with pure arithmetic — no DOM, no reflow. Shrinkwrap, balanced text, per-line variable widths, virtualization of hundreds of thousands of text boxes at 120fps — all fell out as free consequences of solving the root primitive. Twelve competing partial solutions collapsed into one. This is the bar.


1. Rich Text Document Model

The problem everyone is trying to solve: Let users edit formatted text in a browser — bold, italic, lists, embeds, tables, collaborative cursors, undo/redo — without the underlying engine fighting them.

Who is trying to solve it and what each one does best:

  • ProseMirror — The most rigorous document schema and transaction model. Academic-grade correctness. But its API is hostile to newcomers and every "simple" feature requires deep plumbing.
  • Slate — Maximum flexibility. You define your own document schema from scratch. But historical instability — breaking changes across major versions left ecosystems stranded.
  • Lexical (Meta) — Learned from Draft.js's failures. Best performance at scale, smallest bundle. But young, smaller plugin ecosystem.
  • TipTap — Wraps ProseMirror to make it usable. Best developer experience for common cases. But inherits ProseMirror's limitations and adds a layer of abstraction.
  • Quill — Invented the Delta format — a clean operational data structure for rich text. Best for simple editors. But rigid when you need custom blocks.
  • Draft.js (Meta) — Pioneered the immutable document model in React. Now effectively abandoned.
  • CKEditor — Longest-running project. Best enterprise feature coverage. Most battle-tested. But monolithic, hard to customize deeply.
  • Notion, Google Docs, Figma — Each built proprietary engines internally because nothing public was good enough.

The primitive that hasn't been extracted: A document model with conflict-free operational transforms (or CRDTs) that defines schema, selection, input handling, and undo/redo as pure data structures and functions — with no renderer, no DOM, no framework. Every library rebuilds this from scratch and welds it to their rendering layer. The document engine and the editing surface have never been properly separated.


2. Form State, Validation & Dependencies

The problem everyone is trying to solve: Manage the complete lifecycle of a form — field values, validation rules, inter-field dependencies, async validation, dirty/pristine tracking, error display, submission — without the abstraction collapsing under real-world complexity.

Who is trying to solve it and what each one does best:

  • React Hook Form — Best performance (uncontrolled inputs, minimal re-renders). But struggles with dynamic fields and complex conditional logic.
  • Formik — Most intuitive mental model (initialValues, onSubmit, validate). But performance degrades with large forms.
  • TanStack Form — Framework-agnostic ambition, strong TypeScript inference. But young, still stabilizing.
  • Final Form — Subscription-based reactivity, only re-renders what changed. But effectively abandoned.
  • Vuelidate, VeeValidate — Best Vue integration. But Vue-only.
  • Zod, Yup, Valibot — Schema validation libraries that every form library integrates with differently, proving the validation layer hasn't been standardized.
  • XState — Models forms as state machines, which is formally correct. But the learning curve is disproportionate to the task.

The primitive that hasn't been extracted: A dependency graph engine that takes a schema definition (fields, types, rules, dependencies between fields) and, given a set of values at any point in time, computes the complete form state: which fields are valid, which have errors, which are dirty, which are disabled because of another field's value, which async validations are pending. Pure function. No React, no DOM, no hooks. Input: schema + values. Output: complete computed state.

A reasonable objection: reactive signal systems (Solid, Preact Signals) and spreadsheet engines already implement dependency graphs. But signals are a generic reactivity primitive — they don't know what a form is. They don't encode validation semantics, field-level dirty tracking, async validation queuing, or the concept of submission readiness. The gap is the domain-specific layer on top of generic reactivity that every form library reinvents from scratch.


3. Interactive Data Tables

The problem everyone is trying to solve: Display and interact with tabular data — sorting, filtering, grouping, resizing columns, inline editing, row selection, clipboard operations, virtual scrolling over millions of rows — without each feature breaking the others.

Who is trying to solve it and what each one does best:

  • AG Grid — Most complete feature set in existence. Handles millions of rows, server-side models, Excel export. But enormous bundle, expensive license, opaque internals.
  • TanStack Table — Headless. Zero UI, pure logic. Best TypeScript types. But you build the entire rendering layer yourself, and it still assumes React patterns.
  • Handsontable — Best spreadsheet-like editing experience. Closest to Excel in a browser. But heavy and commercially licensed.
  • Glide Data Grid — Canvas-rendered, fastest paint performance. But limited feature set compared to AG Grid.
  • SlickGrid — Pioneered virtual scrolling in grids. Still used in legacy systems. Dated API.
  • DataTables (jQuery) — Largest ecosystem, most plugins. But jQuery-dependent and architecturally from another era.
  • Google Sheets, Excel Online — Built proprietary engines because nothing public could handle the requirements.

The primitive that hasn't been extracted: A table layout and virtualization engine that, given column definitions, a data window, a viewport size, and active sort/filter/group states, computes exactly which cells are visible, their positions, their dimensions, and the scroll geometry — as pure arithmetic. No canvas, no DOM, no rendering opinion. The computation of "what is visible where" has never been separated from "how to paint it."


4. Drag, Drop & Spatial Reordering

The problem everyone is trying to solve: Let users grab elements and move them — reorder lists, transfer between containers, rearrange grids, resize panels — with fluid visual feedback and accessible keyboard alternatives.

Who is trying to solve it and what each one does best:

  • dnd-kit — Best modular architecture. Sensors, collision detection, and sorting strategies are separate, composable pieces. But React-only.
  • Pragmatic DnD (Atlassian) — Best mental model for complex drop targets (trees, boards, nested containers). Framework-agnostic. But opinionated about interaction patterns.
  • SortableJS — Works without any framework. Best vanilla integration. But limited collision strategies, no accessibility story.
  • React DnD — Introduced the concept of pluggable backends (HTML5, touch, test). Best architecture for testing. But API is obtuse.
  • @shopify/draggable — Best event system. Clean lifecycle hooks. But limited to DOM.
  • Framer Motion — Best physics and visual feedback during drag. But only for React and only for simple cases.
  • HTML5 Drag and Drop API — Native. Zero dependencies. But broken on mobile, ugly default ghosts, limited drop feedback.
  • interact.js — Best for freeform positioning (not list reordering). Resize, rotate, snap to grid. But doesn't solve reordering.

The primitive that hasn't been extracted: A spatial reordering engine that, given a set of rectangles with positions, a pointer position, movement constraints (axes, bounds, snap), and container rules (single list, grid, nested, kanban), computes the current drop target, insertion index, and the positions all elements should animate to — as pure geometry. No DOM, no pointer events, no rendering. The math of "if I drop here, what rearranges?" has never been separated from the interaction handling.


5. Layout Transitions (FLIP)

The problem everyone is trying to solve: When elements change position in a layout — items reorder, filters remove cards, routes change — animate them smoothly from where they were to where they are, instead of teleporting.

Who is trying to solve it and what each one does best:

  • Framer Motion layout / layoutId — Most magical developer experience. Shared layout animations across components. But React-only and heavy.
  • Vue <TransitionGroup> — Tightest framework integration. Handles enter/leave/move in one API. But Vue-only, limited customization.
  • FLIP technique (Aerotwist) — The original algorithm. Framework-agnostic in theory. But everyone reimplements it differently.
  • AutoAnimate (FormKit) — One line of code, works on any DOM parent. But limited to simple cases — no cross-component transitions.
  • Svelte animate:flip — Most concise syntax. But Svelte-only.
  • React Flip Move — Focused on list reordering. But abandoned.
  • Barba.js — Best page-level transitions. But doesn't handle intra-page layout changes.
  • SwiftUI matchedGeometryEffect — Native platform gold standard. But Apple-only.
  • View Transitions API — Browser-native, cross-document capable. But limited adoption, coarse control.

The primitive that hasn't been extracted: A transition geometry engine that, given two sets of element rectangles (before state and after state) with identity keys, computes the interpolated position, size, and opacity of every element at any time t — including enter animations for new elements, exit animations for removed ones, and move trajectories for persisting ones. Pure function. No DOM reads, no getBoundingClientRect, no framework. Input: two snapshots + t. Output: rectangle positions.


6. Scroll Virtualization

The problem everyone is trying to solve: Render only the visible portion of a large list, grid, or tree — keeping 60fps scrolling while handling variable-height items, dynamic loading, scroll anchoring, and programmatic scroll control.

Who is trying to solve it and what each one does best:

  • TanStack Virtual — Best headless approach. Framework-agnostic core. But variable-height measurement still requires DOM reads.
  • react-window — Simplest API for fixed-size lists. Lightweight. But no variable-size support without hacks.
  • react-virtuoso — Best variable-height handling. Automatic measurement, grouping, reverse scroll (chat). But React-only, heavier.
  • @angular/cdk VirtualScroll — Best Angular integration. But Angular-only.
  • Clusterize.js — No framework. But primitive feature set.
  • RecyclerListView (Flipkart) — Best mobile recycler pattern (reuses DOM nodes instead of creating/destroying). React Native origin.
  • UICollectionView, RecyclerView — Native platform implementations that remain the gold standard. Proprietary.

The primitive that hasn't been extracted: A viewport-to-index mapping engine that, given a list of item heights (or a function that returns them), a scroll offset, and a viewport size, computes exactly which indices are visible, their y-offsets, the total scroll height, and anchor correction deltas — as pure arithmetic. Text layout has already proven this approach works — virtualizing hundreds of thousands of text boxes at 120fps without DOM by separating measurement from layout. The same pattern applied to generic lists would unify every virtualization library.


7. Responsive Image/Media Packing

The problem everyone is trying to solve: Given N images or media items with known aspect ratios, arrange them in a container to fill the available space — minimizing whitespace, respecting ratios, and adapting to container resizes — without cropping unless explicitly requested.

Who is trying to solve it and what each one does best:

  • Google Photos — Best visual result. Rows of images with consistent visual weight. But proprietary, never published.
  • Flickr Justified Layout — Open source row-based packing. Good results. But unmaintained, limited configuration.
  • Masonry (CSS + JS implementations) — Pinterest-style column packing. Fills vertical space well. But leaves ragged bottoms, doesn't optimize row-by-row.
  • CSS grid-template-rows: masonry — Native proposal. But only in Firefox behind a flag, uncertain future.
  • Muuri — Drag-and-drop + masonry. But focused on interaction, not optimal packing.
  • Photoswipe, Lightgallery — Focus on viewing experience, delegate layout to CSS or simple grids.
  • React Photo Album — Best React integration. Multiple layout modes. But React-only.

The primitive that hasn't been extracted: A 2D bin-packing engine for aspect-ratio-constrained rectangles that, given N ratios, a container width, and constraints (max row height, min/max items per row, acceptable ratio deviation), computes the optimal row breaks and per-item dimensions — as pure arithmetic. Input: ratios + constraints. Output: positions and sizes. No DOM, no rendering.


How to read this list

Each entry describes a gap, not a project. The primitive column is a specification of what the solution should look like — not the solution itself.

The pattern that connects all of them:

  1. The browser (or proprietary software) does this internally but doesn't expose it.
  2. Multiple libraries have rebuilt it from scratch, each one getting a different subset right.
  3. The core computation is pure — it's arithmetic, geometry, or graph traversal — but every existing implementation tangles it with rendering, framework bindings, or DOM dependencies.
  4. Extracting the primitive would collapse the need for multiple libraries and unlock use cases that are currently impossible (canvas, video, native, WebGL, server-side).

If you solve even one of these at the same depth that text layout and measurement has been solved, you will have advanced the field.


This document is maintained as a living reference. If you believe a primitive has been solved or a new candidate should be added, open a pull request.


Related posts
Entradas antiguas

👁️ Focus on the step in front of you
not the whole staircase 💆🏻