Skip to content

Components has no UI-kit-free entry point: the Arc binding surface only ships inside the PrimeReact package #162

Description

@woksin

What happens

There is no way to consume Cratis's Arc-binding behavior without also taking a UI kit and its licence.

@cratis/components is the only package that carries the command/query binding surface consumers actually build screens with — CommandDialog, CommandForm fields, DataPage, the query-driven tables, Notifications. Every one of those arrives through a package whose peer set is PrimeReact 11, which is PrimeTek's commercial PrimeUI (see #161). A consumer who has their own design system, or who cannot accept a commercial peer, has no supported entry point — they either take the licence or reimplement the binding layer.

This is not a new idea; it is the generalization of one already stated in this repository. #109 says it for tables:

"The seam's value is that the UI library is a swappable detail. Today the abstraction that delivers that (TableRenderer + bindQuery) lives inside the PrimeReact library it is meant to abstract away … Moving it there would let a Cratis app bind Arc queries to its own table with no PrimeReact dependency, and leave @cratis/components as purely a rendering library — one implementation of the contract, not its owner."

That principle is right and it is not table-specific. This issue asks to apply it library-wide.

Evidence

Measured against the published @cratis/components@3.4.0 artifact (dist/esm .js + .d.ts), with primereact@11.1.0:

  • 171 emitted modules; 45 import primereact/*, @primereact/* or @primeuix/*.
  • 30 .d.ts files import a PrimeReact/PrimeUIX type; 29 are reachable from a public entry point.
  • 27 distinct PrimeReact symbols are public API: 18 types reachable only through the pt/ptOptions pass-through (one mechanical pattern across ~56 declaration sites), 8 semantic types, and 1 runtime value.

The encouraging half of the same measurement — the work is already substantially done:

  • ~75+ exports are already PrimeReact-free: all of Toolbar, all of Filter, all of PivotViewer (pixi.js-based), the TimeMachine core, and the Cratis-owned Column / StepperPanel / MenuItem / DataTableSelectionChangeEvent / DataTableFilterMeta markers.
  • ~40 exports are thin — a single widget behind Cratis-owned props (all of Display, 13 of 15 CommandForm fields, Button, Dialog, Tooltip, DatePickerInput, TablePaginator).
  • The v10→v11 migration already produced most of the neutral vocabulary as a side effect.
  • @cratis/arc.react is already fully headlessreact is its only peer, and it contains zero PrimeReact references. asCommandFormField / WrappedFieldProps<T> already live there, exactly as Prove a renderer-neutral Arc React query/table state seam #109 observes.

So the coupling that blocks a UI-kit-free entry point is concentrated, not diffuse. Four places carry it:

  1. CratisComponentsConfig = Partial<PrimeReactProps> — the configuration surface is PrimeReact's (filed separately).
  2. The styled subpath — styledMode + primeReactStyles (74 @primereact/styles/* imports) + CratisPreset. PrimeTek's token system by construction; correctly adapter-owned.
  3. toast, re-exported verbatim from primereact/toaster (filed separately).
  4. The data-table filter/selection engine — DataTableCore + ColumnFilterMenu (filed separately).

What it costs a consumer

Real, and paid in both licensing and maintenance cost.

Suggested fix — the seam

Generalize #109 from tables to the library:

  1. Publish a UI-kit-free entry point carrying behavior only — command execution and validation state, observable-query binding, paging/sort/filter state, dialog and stepper orchestration, toast dispatch, locale plumbing, error mapping, and the neutral marker types that already exist. Whether it lands as @cratis/arc.react (where asCommandFormField and useQueryWithPaging already are, and where Prove a renderer-neutral Arc React query/table state seam #109 proposes putting TableRenderer/bindQuery) or as a new @cratis/components.core is an implementation choice this issue takes no position on. The requirement is that it installs with no UI-kit peer and no licence obligation.
  2. Keep @cratis/components as exactly what Prove a renderer-neutral Arc React query/table state seam #109 names it — one implementation of that contract. Its public API stops naming PrimeReact types (the four items above), and PrimeReact becomes an adapter-local detail rather than a library-wide one.
  3. Document the headless contract to the standard an adapter would have been documented to, so "bring your own UI kit" is a supported path rather than an inference.
  4. Consider shipping a conformance suite any UI implementation can run to prove it satisfies the contract.

The smallest useful first step is the one #109 already identifies: relocate the pure contract types, which carry no PrimeReact and therefore no circular-dependency problem.

What is explicitly not being asked for

  • Not additional first-party UI adapter packages. Public evidence is strongly against a small team maintaining several: React Admin shipped one UI package (ra-ui-materialui) in ~10 years while shipping 50+ data providers under the same adapter pattern, and put its second UI story (shadcn-admin-kit) in a separate repository. Refine shipped four UI adapters and they carry 13 / 5 / 2 / 0 adapter hooks respectively, with @refinedev/chakra-ui at 1,665 weekly downloads and @refinedev/mantine at 1,092 against a core at 148,342 — a 48:1 skew between first and fourth. One adapter, plus recipes for anyone else, is what the evidence supports.
  • Not dropping, replacing or vendoring PrimeReact, and not reverting the peer-dependency change.
  • Not any change to PrimeTek's licensing, or any mechanism that suppresses the licence banner.
  • Not a breaking change to DataTableForQuery / DataTableForObservableQuery / CommandDialog public props — the same non-breaking constraint Prove a renderer-neutral Arc React query/table state seam #109 sets for itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions